User guide
Your Visit Manager guide
1. Set up My account
Enter your home and secondary locations. Typing an address triggers IGN/BAN suggestions. Select and double-click a suggestion, or press Enter / Confirm address, to fill available fields. Check house number, city and postcode; street-level results require a house number. Enter building/office extras yourself. GPS coordinates for accounts and locations come from the service.
Set field, home-working and rest days, working hours and lunch. Choose a default visit duration from 5 to 480 minutes. Preference changes may remove future appointments and create a snapshot; confirmed appointments are protected. Read the notice before changing your schedule.
2. Manage the portfolio
Filter by city, name or priority. Double-click a row or use Edit. Record address, last visit, notes, priority, interest and frequency. Duration 0 inherits My account's default; a positive duration overrides it. Exclude an account with Do not propose a visit. Save changes. CSV import adds rows, so check duplicates.
3. Simulate and publish
Select a horizon and automatic or manual account selection. Calculate and review travel, unplaced visits and routing sources. Shared addresses are grouped. Estimated travel can replace unavailable IGN routing; live traffic is not included.
Simulation does not change your calendar. Explicitly add or replace appointments for the horizon. Confirmed/completed appointments are protected and conflicts need resolution. Published appointments also appear in account details.
4. Use the calendar
Switch between day, week and month. Scroll weeks horizontally and times vertically; headers stay visible. Double-click free space to edit the day, or an appointment to see account, address, date, times, duration and status.
Confirm, remove confirmation, record completion with actual duration and notes, report absence/unavailability or cancel. Absence opens rescheduling. Future visits cannot be completed. Archives and simulations are read-only. Cancellation removes the planned appointment from the account; completed visits remain in history. Past uncompleted appointments can be cancelled together.
5. Save and export
Save a date horizon as a snapshot and select it for consultation. Choose start/end dates to export ICS and import into your calendar software. Replacement behavior depends on the receiving application; this is not automatic synchronization. Route exports and printing are also available.
6. Help and limits
The app runs locally on your Mac. Sessions may remain active for 12 hours; log out on shared devices. Subscriptions and invoices are preparatory; payments are disabled. If IGN is unavailable, retry or enter manually and verify the location before planning. Publisher contact: xtr-conseil@orange.fr. The regeneration prompt accompanies this version; Git is the reference for exact code restoration.
Identity and sign-in
Click the top-right initials to open My account and set your first/last name. This does not change your calendar. In another browser, sign in with your existing account instead of registering the same email again.
On phones
Use Menu to open a section or sign out. Initials open My account. Scroll tables and calendar weeks horizontally; forms adapt to screen width.
Forgot password
On sign-in, click Forgot password, enter your email and ask the local administrator for a code. No automatic email is sent. Enter the code and the new password twice (at least 12 characters). The code expires after 30 minutes and all your sessions will be revoked.
Sorting and preferred time
Portfolio sorting: name, city, last visit or next scheduled visit, ascending/descending; missing values always last. Next visit is a future appointment in the validated calendar, not a theoretical due date. Optional preferred time per account: start must fall within the profile tolerance (default ±30 minutes, range 0–240). Opening hours, lunch and return constraints remain active; accounts with no feasible slot remain unplanned. Existing routes are unchanged: regenerate simulations. No active XTR Conseil sender confirmed; local recovery remains available.
Remote testing
An administrator must deploy staging before a link is available. Use the HTTPS URL and individual credentials delivered privately. The Staging banner indicates fictional data; do not enter real data. Self-registration is disabled.
Fixed appointments — version 0.12.0
In My calendar, double-click a day and select Add a fixed appointment. Enter a title, start, end and location (home or a secondary location saved in My account, using IGN/BAN address lookup). Add up to 24 time slots per day. Save the day and regenerate the simulation.
These appointments keep their exact times and locations: planning reserves travel time to reach them and leave afterwards. Account visits are scheduled around them. An impossible itinerary returns an error; fixed appointments are never moved. Slots must fall within the day’s working hours and cannot overlap each other or lunch; adjust lunch explicitly when needed. Non-field days remain excluded from routes.
Fixed appointments appear in ochre in the calendar even without a route, and are included in ICS exports and archives. They survive visit cancellation and restoring day defaults. To remove one, open the day, select Remove this time slot, and save. As with other daily settings, saving archives and clears that day’s route for recalculation; confirmed visits remain protected. Fixed appointments do not count as account visits or update last-visit dates.
Portfolio and company — version 0.13.0
The “Visit” filter offers “Visit”, “Do not visit” and “All”. It combines with search, town, priority and status. “Hide filters” saves space while keeping criteria and selection active; “Show filters” opens them again. My account includes an optional Company field, saved with personal identity without changing the calendar.
The test portfolio was expanded from the public RPPS extract dated 17 September 2026: 1,717 practice records, 1,542 practitioners and 127 towns in Haute-Garonne. Each record represents a practitioner and a practice structure; multiple practices produce multiple accounts. Coverage depends on the published source and is not a guarantee of real-world completeness. Existing accounts, notes and visit history are preserved. Personal records remain local and outside Git.
For this test dataset, the eastern sector means east of the Ariège, then east of the Garonne downstream of their confluence, without a radius limit. Located accounts in this sector are marked “Do not visit”. Existing exclusions elsewhere are preserved. Unresolved addresses are not geographically classified and need checking; street-level locations remain approximate. The exclusion checkbox remains editable in each account.
Places and calendar import — version 0.14.0
In My account → My places, use Add a location, enter a name (Office, South branch, Home 2…), search IGN/BAN and validate a suggestion to fill the complete address and coordinates. Save your settings. The location name appears in departure, return, lunch and fixed appointment selections.
For a fixed appointment, select a saved place or Other location. For a one-off place, enter its name and search and validate its address. It belongs only to this appointment and does not change your usual places. Routing uses its coordinates. Enter times as HH:MM (9:30 is also accepted and saved as 09:30). Errors appear inside the dialog: end after start, within the working day, with no overlap with lunch or another fixed appointment. Adjust that day’s lunch break if needed.
My calendar → Import an .ics calendar previews appointments before importing them as fixed commitments. Check places, select a saved or one-off location for each row, and remove unwanted rows. Import is atomic: a conflict blocks the whole batch without partial additions. Previously imported appointments with the same identity and start timestamp are ignored. Unconfirmed tours on affected days are archived then removed for recalculation; confirmed visits remain protected.
Current limits: 500 KB file, 200 appointments and 24 fixed slots per day. Dated occurrences with start and end on the same day and minute precision are converted to Europe/Paris. Floating times use Europe/Paris. Recurring series, all-day and overnight events are explicitly rejected: export individual occurrences. This is not a live Google, Apple or Outlook synchronization. Later source-calendar changes do not automatically update imported events.
Personal backup — version 0.15.0
My account → Back up and restore my data offers Export my data and Import a backup. All available categories are selected by default: settings/identity/places, accounts, calendar/leave/archives, completed visits/postponements, simulations and billing/subscription. Uncheck unwanted categories. Export downloads a Visit Manager JSON file; CSV remains for importing accounts only.
Import accepts a backup belonging to your current account, not another user’s backup. Choose a file and categories, then Check before restoring. The summary lists record counts by category. Tick the confirmation and restore. Selected categories are replaced, even when empty in the file; other categories are preserved. Categories missing from the file cannot be selected. Accounts with linked visits or plans require the related categories to be restored together; settings require calendar and simulations when those data exist. Inconsistent dependencies are rejected rather than breaking links.
A complete pre-restore recovery copy is retained locally in data/.user-backups with restricted access. It can be imported to undo the restoration. Restore is atomic and other users remain untouched. Passwords, sessions, reset codes and technical caches are excluded; the login email is unchanged. Files contain personal data: store downloads in a private location. Limit: 50 MB per file. The automatic local copy does not replace a downloaded backup kept off the computer.
Account owner — version 0.16.0
Each account has an optional commercial owner (150 characters), displayed in the list and included in search, CSV import through the owner column and JSON backups. This label grants no user access. In the Haute-Garonne test dataset, geolocated accounts west of the Ariège then Garonne default to Magalie Rousselin; an existing owner is preserved. Eastern accounts and those without coordinates are not assigned automatically. The RPPS preload uses the same rule.
Durable appointment history — version 0.17.0
Under History and visits, Past appointment history lets you browse a date range. It retains the name and address known at capture time, times, appointment type, status and visit report when available. Fixed appointments are included. Planned or confirmed appointments without a recorded outcome remain explicitly unreported; elapsed time never marks them completed.
A dedicated historical register is stored in the local database separately from the active calendar. It is updated at startup, on opening the workspace and around modifications. Previous appointment versions are retained; the list shows the latest known state. Clearing, cancelling or recalculating the calendar does not delete the register. Existing past appointments are recovered from remaining schedules, completed visits and archives; information already deleted without an archive cannot be reconstructed.
The register is private to each user and included in the Completed visits and postponements backup category. Version 2 JSON backups contain it; version 1 files remain accepted. An explicitly confirmed restoration of that category also replaces its history, with the usual pre-restore recovery copy.
Staging is available at https://visit-manager.fr as of 17 September 2026. OVH VPS deployment uses Caddy HTTPS, a loopback-only Python service, a separate demonstration database and disabled public registration. Individual credentials are shared privately; real portfolios require explicit owner authorization. Daily SQLite backups retain 14 copies on the VPS, in addition to OVH backup. Automatic email password recovery remains unconfigured.
Beta update: on the owner’s explicit request, a real portfolio may be transferred to their individual account. Require VISIT_STAGING_ACK=authorized-real-data and the matching staging_meta purpose=authorized-real-data marker; demo-only remains the default for fictional databases. Retain HTTPS, per-user isolation and closed registration. Back up before import and preserve account fields. The banner now states “Private beta”. Never commit secrets or data exports.
Presentation: the workspace header groups navigation on the left and language/account controls on the right. Explicit spacing, divider and a two-row mobile layout; French and English labels.
Version 0.18 — Appointments at the same address
Every simulation calculation or recalculation after rescheduling asks “Group appointments for accounts at the same address?”. Accept to share one standard duration from My account between eligible accounts with a zero custom duration. Decline to retain each full visit duration. Three standard accounts share 45 minutes as 15 each; two share it as 23 and 22 minutes, plus the full duration of any custom appointment. Minute rounding preserves the exact total. Custom durations stay individual even when equal to the standard duration.
Each appointment retains its own account, status and history. Only selected, visitable and available accounts participate. Opening hours, preferred times, lunch and fixed appointments remain respected; incompatible shared slots fall back to individual visits. Group size is limited to the slot’s number of minutes to prevent zero durations. The choice is stored with the simulation for saving and agenda transfer and is asked again on the next calculation.
Version 0.18.1 — Double-click appointment status
In the current calendar, double-click a visit and choose its status: Planned, Confirmed by account, Completed, Absent / reschedule or Cancelled. Click Save status. Completed opens actual duration and notes and is available from the appointment date. Absence opens rescheduling; cancellation requires confirmation. Errors remain visible in the dialog. Simulations and archives are read-only; finished visits retain their history.
Version 0.18.2 — Readable travel blocks
Travel blocks use the available day-column width instead of a 16-pixel strip. Start and end times come first, followed by duration and address when height permits. Very short trips keep a height proportional to their duration; hover or keyboard focus expands their details. Times and stored data are unchanged.
Version 0.18.3 — Saving locations
In My account, “Save my locations and settings” stays visible while scrolling, with another button at the bottom. Edits, adding a location and selecting an IGN address mark the form as unsaved. Leaving the section, following a same-tab link or signing out asks for confirmation: No keeps the draft, Yes discards it. Escape means No. Clicking My account again does not reset the form. Reloading or closing the page uses the browser’s native warning and wording. Protection clears after successful saving, remains after errors and preserves edits made during a save. It covers the locations and schedule form; other forms have separate save actions.
Version 0.19 — Automatic saving
My account locations and schedules are saved as a server-side draft one second after the last edit. Incomplete addresses are preserved without being validated; “Save my locations and settings” explicitly applies the form with the usual checks. The draft returns when My account is next opened. The indicator confirms automatic saving; on network failure, keep the page open and retry by editing a field. Closing before saving completes triggers the browser warning. Explicitly discarding removes the draft.
Every successful calculation automatically retains the latest simulation per user. It is restored on loading and available in Simulation calendar; no appointments are published. Review before transfer, especially after changing settings. Named simulations remain separately available. Drafts are included in full server backups, not in category-based JSON exports. User imports clear drafts to avoid stale references. Concurrent tabs use last-save-wins behavior; use one editing tab.
Version 0.20 — Account contact details
Account forms and portfolio rows show editable Business phone, Business mobile and Business email fields. The CSV template appends phone, mobile and professional_email without renaming existing columns. JSON backups preserve these fields.
Administrative enrichment: scripts/enrich_contacts.py uses Annuaire Santé RPPS (RPPS + practice identifier) and Ameli (full name + normalized street + postal code + town). Only empty fields are filled from unambiguous public values; published French 06/07 numbers are classified as business mobiles. Existing values remain unchanged and each addition records its source and verification date. No email guessing or MSSanté mailbox enrichment for business contacts. Preview is the default; --apply backs up SQLite and updates only the selected user without changing calendar, notes or owners. Source files and databases stay out of Git. Sources: https://www.data.gouv.fr/datasets/annuaire-sante-ameli and https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.21.0 — team licences and AI professional lists
The /licence page presents individual subscriptions and annual-only team packs: up to 5 users including the owner, proposed at EUR 699 including VAT; up to 10 at EUR 1,199 including VAT. Team prices require commercial approval before activation. For more than 10 users, contact XTR Conseil. Owners add or remove existing registered users. Workspaces stay separate and private. Seat limits are enforced on the server. Administrators edit prices and bilingual descriptions; zero hides an offer.
The /administration page is restricted to server-configured administrator emails. It covers users, blocking, free grants, password resets, estimated storage, workspace consultations, orders, invoices, prices and AI lists. A free grant does not issue a paid invoice. Manual payments require a receipt reference and explicit confirmation. PDF invoices are generated from order snapshots and retained. JSON invoice data prepare an accredited electronic invoicing platform connection; they do not perform regulatory transmission.
To prepare an AI list, enter a profession and area, choose a limit of 1 to 100, supply public source text as needed, build and edit the prompt, then save it. Under Providers, configure an exact model identifier and API key for OpenAI, Claude, DeepSeek or Gemini. Keys are encrypted on the server and never returned to the interface. Generation requires confirmation of a potentially billable call. OpenAI uses web search if supported by the model; the other three adapters analyse supplied text without web browsing in this version. Results may be empty and are never described as exhaustive or certified.
Results remain drafts for review. Open sources, check contact details, select rows and a target user workspace, then confirm import. Rows without a source cannot be imported. Matching name/address duplicates and repeated imports are avoided. AI-generated coordinates are discarded: validate addresses before route planning. No portfolio or calendar is automatically sent to an AI provider.
Delivery status: local development, adapters tested with simulated responses; no real AI call without keys. Stripe and no-reply@visit-manager.fr email require configuration and end-to-end verification. Consumer sales remain disabled. This preparatory version does not claim a deployed administration subdomain or commercial launch. Existing data are preserved. Grant appropriate access to beta testers before enabling licence enforcement.
Version 0.21.1 — official company search
Account creation/editing, My account billing details and licence checkout now provide dynamic French company search by name, SIREN or SIRET. Results show each establishment’s SIRET, address, status and head-office marker. Selecting a result fills the name, address, SIREN, SIRET, available VAT number, activity code and legal category. Review and save the form: selection alone does not save. Fields remain editable and manual entry remains available on service failure. The public Company Search API may omit non-public entities. No VAT number is calculated; an API-supplied number is not VIES validation. Only the search text is sent to the official service. Stale responses are discarded, requests are rate-limited and authentication is required. SIRET and VAT are preserved with account or billing records.
Licence presentation: smaller headings and prices, five offers aligned on wide screens, adaptive tablet and phone layout, and a compact centred sign-in form.
Version 0.22.0 — Home, purchase and contact
Paid plans and the trial appear on the home page using catalogue prices. The selected plan persists through registration or sign-in and billing details. The unchecked “I have read and accept the terms of sale” box is mandatory before Stripe; the terms version and acceptance timestamp are recorded. No payment starts without Stripe and approved commercial settings. Existing private-registration and consumer-sale gates remain in place.
The public contact form offers information, technical, licence/billing, improvement, privacy and a custom topic. Each request receives a VM-year-number reference; retrying the same submission does not duplicate it. Administrators can view the latest 200 requests and mark them new, in progress or closed. Notifications to xtr-conseil@orange.fr and acknowledgements are queued. The requested sender is no-reply.xtr-conseil@orange.fr, confirmed by the publisher to exist. SMTP still needs configuration; requests are recorded without sending email when it is unavailable.
Terms and privacy prohibit marketing or commercial exploitation, describe backups on OVHcloud infrastructure and recommend personal exports. They distinguish portfolio access through the management UI from possible technical server access; no end-to-end encryption guarantee is made. Communications are limited to the service, contract and replies to user requests.
Version 0.22.1 — Contact history
Administration retains received messages and a dated history of actions and their author. Track case status (New / In progress / Closed) separately from response status (Awaiting response / Answered / No response needed). An internal note is required when marking a request answered or unnecessary. It records a response sent separately or a reason; saving sends no email. Automatic acknowledgement is not a response. Original messages and earlier notes remain unchanged.
Search by reference, name, email or subject and filter by case and response status. Requests load in pages of 50, including older records. Existing cases retain their status and receive a history baseline without inventing earlier responses. Revision checks reject stale concurrent updates.
Mentions légales / Legal notice (18/09/2026) : préserver la page bilingue mentions-legales.html, legal.css et les liens depuis les pages publiques ; éditeur XTR Conseil et hébergeur OVH pour les instances hébergées. Keep the bilingual public legal notice and links, separately from service-specific terms and privacy. See docs/MENTIONS_LEGALES.md.
Version 0.22.2 — Administration security
The Security tab changes the signed-in administrator’s own password: current password, new password (12–256 characters) and confirmation. Verification is limited to five attempts per fifteen minutes. Passwords are hashed and excluded from audit records. Success invalidates every session and reset token for this account; signing in again is required. Other users remain unchanged.
Support is prepared for admin.visit-manager.fr: configure VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, add DNS and the Caddy site in deploy/staging/admin.Caddyfile.example, then verify HTTPS. Preserve the subdomain Host header. Origins are validated separately per host and cookies remain host-only. Non-administrative APIs are rejected on the subdomain. Activated on 18 September 2026: OVH DNS, HTTPS certificate and administrator access verified. The existing /administration path remains available.
The portfolio uses an aligned action bar, multi-column filters and a more compact list. On phones, controls reflow and the table retains horizontal scrolling. Hide/Show filters remains available.
CSV template downloads headers in the active language, with a fictional second row to replace or delete. Template formats opens per-column guidance (YYYY-MM-DD dates, HH:MM times, durations in minutes, codes and phone numbers imported as text). Imports accept French, English and historical technical headers regardless of interface language. Decimal commas are accepted for coordinates. Duplicate headers and malformed rows are rejected before insertion.
User creation and temporary access (12 hours)
The administrator creates an account, selects its language and receives a temporary password displayed once. They can share it directly or use “Send temporary password”: this generates a fresh secret, invalidates the previous one and emails instructions. The user must choose a different password on first sign-in before accessing any data. After 12 hours, issue a new invitation. Passwords are not stored in clear text in logs or mail queues. Check the SMTP outcome; failed or uncertain delivery is reported. Creation does not send email automatically.
Functional documentation
Visit Manager 0.17.0
Documentation française (README.md)
Local sales route planning application. French/English interface inspired by the XTR / AO Manager suite.
Start
Python 3.10 or later, with no external Python dependencies.
python3 server.py
Home: http://127.0.0.1:8790/ ; sales workspace: http://127.0.0.1:8790/app.
On macOS, launch.command opens the home page and starts the server. Create a login from the workspace. Data persists in data/visit.sqlite; each user has their own portfolio, scenarios and history.
French and English
The FR/EN switch works on the home page and every screen. The browser remembers the choice. Direct links are available at /?lang=fr, /?lang=en, /app?lang=fr and /app?lang=en. Account names and notes retain their original language. Native browser controls (date and file pickers) follow the browser's own language.
Labels and messages live in web/messages.json, server errors in translations.py. Route CSV headers and ICS descriptions follow the selected language; import-template headers follow the interface language while historical technical headers remain accepted. All future features must include both languages (see AGENTS.md).
Portfolio
The portfolio opens with its filters and list. “New account” opens the form; “Edit” opens the selected account. Saving or closing returns to the list.
Each account includes a name, full/structured address, priority, business interest, frequency, duration, daily availability, declared last visit, notes and exclusion from proposals. The automatic IGN/BAN search fills address fields after a suggestion is selected and confirmed. Editing the address clears coordinates to avoid retaining an old location.
CSV import adds rows without deduplication. Template headers follow the interface language; historical technical headers remain accepted. Imported RPPS metadata and professional contact details are retained when editing. Working CSV files under exports/ remain local and are not tracked on GitHub.
Planning and postponements
Use automatic recurring planning or manually select accounts through filters. Manual mode schedules one visit per account during the period, even before its next due date. Scenarios can be saved, reopened, edited and deleted without removing completed visits.
“Absence / unavailability” on a route opens the postponement form. The same action is available under “Completed visits”. Enter the original date, reason, next available date and optional notes. An absence cannot be reported for a future date; future unavailability is allowed.
The account is excluded from the original date inclusive until the next available date exclusive. A postponement does not create a completed visit. The application recalculates a proposal for the current/future part of the planning period, respecting availability and return constraints. If starting coordinates are missing, it records the event and asks for them. If the next available date is beyond the planning period, it asks you to extend that period. Unscheduled visits are explained.
An actual completed visit closes earlier follow-ups for that account. Saved scenarios remain snapshots: those containing a postponed visit display a warning and cannot be exported until regenerated. Save the new result to retain a new scenario.
Security and limitations
Local prototype, bound only to 127.0.0.1. Scrypt passwords, random sessions stored as hashes, expiry, HttpOnly/SameSite cookie, CSRF/origin/Host protections, per-user resource ownership and persistent rate limits. IGN searches use a fixed HTTPS endpoint with TLS verification, bounded response size and timeout; only the searched address is sent to the official service.
Not yet a SaaS release: password recovery, MFA, organisations/roles, public HTTPS and deployment validation remain to be implemented. IGN road routing has no live traffic; fallback estimates use straight-line distance × 1.3 at 50 km/h. The heuristic does not guarantee optimal routes. No automatic account confirmations or locked appointments.
ICS exports Europe/Paris appointments in UTC with stable identifiers; duplicate handling depends on the receiving calendar. Print/PDF uses the browser. Changing FR/EN preserves form values and selection.
Verify
python3 -m unittest discover -s tests -v
node --check web/app.js
node --check web/i18n.js
The tests cover recurrence, 300 accounts, constraints, exclusions, imports, completed visits, isolation, CSRF, postponements, date rules and bilingual coverage. Browser checks covered EN/FR home pages, the portfolio with its form hidden, manual selection and postponement with recalculation. GitHub CI runs tests on every push and pull request.
Calendar and locations — version 0.4
In My account, enter My home, then secondary locations (office, hotel, etc.). The official IGN search lets you select an address and its coordinates. Departure, return and lunch venues are independent: departure and return default to home, while lunch defaults to no fixed location. Configure daily times, lunch start and duration (0 disables lunch). Without a fixed lunch venue, choose the previous or next appointment; when there is no next appointment, lunch uses the last reached location.
My calendar offers day, week and month views. Double-click a day to change that day’s locations, times or lunch duration, or restore defaults. Travel to lunch and the return venue is included in estimates. Lunch time is reserved: visits and travel do not overlap it. The engine uses a heuristic with IGN routing and geographical fallback (see version 0.6).
Calendar ICS export covers added routes within the chosen period (maximum 367 days): visits, travel, lunch, departure and return. Events remain tentative. Europe/Paris times are exported in UTC with daylight-saving adjustments, stable identifiers and UTF-8 line folding. Import into Apple Calendar or Outlook; automatic two-way synchronization is not provided. Days without routes display their settings but do not generate ICS events.
Profiles, daily exceptions and calendar routes are user-scoped and protected by sessions and CSRF. The migration adds three tables without changing existing accounts. Entered addresses are searched through IGN; other profile data stays local.
The Town filter is an alphabetical, deduplicated list of towns in your portfolio, refreshed after imports or edits. All towns clears the filter. In My account, each location has number, street, address supplement, postcode, town, INSEE town code and country fields. Confirming an IGN suggestion fills these fields and latitude/longitude from official geocoding. Manually changing the geographical address clears its coordinates so it must be located again. Changing only the address supplement does not clear them. Legacy free-text addresses remain supported.
If no home is saved, the address fields and IGN search appear directly in the day dialog, without leaving My calendar. Saving stores the home and daily settings together. Enter/Space on a day heading also opens the dialog.
Simulation no longer asks for a departure address or coordinates: it uses My calendar locations. If no location is set, configure it by double-clicking a day.
Geocoded accounts can retain the IGN source, date, returned label, type and score. Street/locality results show “Approximate location”. Unresolved addresses keep empty coordinates and remain to be located. Geocoding data and reports stay local and are excluded from Git.
Departure and desired return times are no longer entered in the simulation. Calculation uses calendar defaults and each day’s exceptions.
Calendar, history and licences — version 0.5
My account defines each weekday as field work, working from home or rest. French metropolitan national public holidays are blocked by default. Leave periods entered in My calendar prevent travel proposals. Times and locations remain adjustable per day. Personal test preferences are stored only in the local database.
Each proposal opens a separate simulation calendar. Explicitly choose to replace planned visits within its horizon or add them. Adding preserves existing appointments, deduplicates accounts within each day and rejects visit, travel, lunch or return conflicts. Completed visits are preserved. A snapshot is saved before transfers, preference changes or leave additions. You can also name and save a past calendar, view it read-only and export its period to ICS. Changing general preferences preserves past calendar days and invalidates future routes.
About displays XTR Conseil, its logo, contact information and application version. The subscription area prepares individual monthly/yearly licences and billing details. Prices are not configured and payment is disabled. The downloadable document is a non-issued invoice preview without an invoice number or amount. No paid subscription is activated. Payment-provider integration, server-side payment confirmation and final invoice issuance remain to be implemented after commercial decisions.
The bilingual terms of sale (web/cgv.html) are a draft to complete before commercialization, including pricing, renewal/cancellation, professional/consumer scope and data-processing arrangements. See terms preparation (docs/CGV_PREPARATION.md).
Eligible accounts at the same address are prioritized consecutively, with no travel or additional setup time between consecutive visits on site. Durations and tracking remain individual. Availability, exclusions, due dates and breaks still apply; unselected accounts are not added in manual mode. Matching ignores case, accents and punctuation; a town alone or identical coordinates are insufficient. Regenerate existing simulations to apply grouping.
Road routing — version 0.6
IGN / Géoplateforme routing is enabled by default without an API key. The car profile uses the BD TOPO road network, choosing fastest routes or shortest routes for distance optimization. Durations are modelled without live traffic. Only endpoint coordinates are sent to https://data.geopf.fr/navigation/itineraire, never account names or notes. Choose geographical estimates for offline use.
The heuristic proposes stops, refines the selected legs through IGN and recalculates appointment, lunch and return constraints (up to four passes). It does not exhaustively optimize all account pairs. A directional local cache lasts 30 days and is excluded from Git. Requests are spaced at least 260 ms apart, with up to 60 new legs and about 25 seconds of network work per calculation. Missing legs retain geographical estimates: calendars, proposals and exports identify IGN, mixed or estimated routing. Recalculate to reuse the cache and refine larger horizons. Adding to a calendar validates legs against the cache and identifies remaining estimates. Regenerate older simulations.
Service documentation: https://cartes.gouv.fr/aide/fr/guides-utilisateur/utiliser-les-services-de-la-geoplateforme/calcul-itineraire/
Calendar display hours are configured in My account (default 07:30–19:30). Time rows align across all days; day/week views show only the selected hours. This personal display setting preserves routes and working hours. Events outside the range remain available in month view and exports.
Account forms and portfolio rows display appointments from the current validated calendar, including date and times. Simulations and archives do not populate this field. A completed visit on the same day removes the appointment from this list. Individual cancellation, reset over a period and a proposal to cancel past uncompleted appointments require confirmation. Each cancellation archives the calendar and preserves completed visits. Other appointments keep their times. Remaining travel is recalculated after cancellation, with a conflict alert if needed (see version 0.8).
Week view scrolls horizontally across nine weeks (four before and four after the selected week). The month/year banner and dates follow the browsed week; arrows continue navigation. Day headers separate weekday, date number and month. The .ics export above the grid uses the entered period and selected source (current calendar, simulation or archive), independently of the visible week.
Successful API responses are sent after the database transaction commits, so sessions and changes are available to the next request immediately. A regression test introduces a transaction-exit delay to reproduce the race condition.
Appointment follow-up and recalculation — version 0.8
Calendar appointments distinguish Proposed (simulation), Planned (validated transfer), Confirmed by account, Completed, Absent and Cancelled, using both colours and labels. Click an appointment in the current calendar to confirm, remove confirmation, record completion, report absence or cancel. Confirmation is an explicit user action; no message is sent to the account. Archives and simulations remain read-only.
Cancellation or absence preserves other appointment times and statuses. Travel is recalculated automatically through IGN with labelled geographical fallback, respecting lunch and return constraints. If fixed times conflict, appointments remain, obsolete travel is removed and an alert offers recalculation. An absence no longer removes entire days for other accounts. New route snapshots retain their locations for later recalculation.
Transfers cannot silently move or delete confirmed or completed appointments. Changes to preferences, days or leave affecting confirmed appointments require removing their confirmation first. Explicit cancellation remains available. Cancelled/absent appointments remain visible as history and are excluded from planned visits; replanning the same account on the same day replaces this history. ICS distinguishes TENTATIVE, CONFIRMED and CANCELLED; importing these updates depends on the receiving calendar. Recorded completed visits remain the sole source of completed commercial activity.
In day/week views, each day header stays visible while scrolling vertically inside the calendar and follows its column when scrolling horizontally.
Double-click an appointment in the current calendar to view its details and actions. With a keyboard, press Enter or Space on the appointment.
Details show the account, address, date, times, planned duration and status. A completed visit can be recorded in this dialog with its actual duration and notes. Simulations and archives can be viewed read-only.
IGN/BAN searches run automatically after a 650 ms typing pause for account, home, secondary location and billing addresses. Select a suggestion to confirm and fill in fields (including GPS for accounts and locations). Stale responses are ignored. No result or a service failure does not validate an address: manual entry remains available, particularly outside France. Separate address-extra, name and notes fields are not included in these searches.
In the portfolio, double-click a row to edit the account. The Edit button remains available, including for keyboard and mobile use. Selection checkboxes retain their usual behavior.
Version 0.9.0: double-click address lists, zero duration inherited from My account, user/functional/technical documentation and regeneration prompt at /docs-en.html. Run python3 scripts/build_docs.py before every push.
Sign-in: an existing email prompts the user to sign in without recreating or replacing the account. The top-right initials open My account. First and last name determine initials; otherwise the email is used. Identity saves separately without changing the calendar.
On phones, sign-in has a compact header without workspace navigation. After sign-in, Menu reveals sections and sign-out; selecting a section closes it. Touch fields use 16 px text, narrow forms use one column and tables retain horizontal scrolling.
Forgot password records a local request without disclosing account existence. The administrator runs python3 password_reset.py address@example.com and delivers the code privately. No automatic email. Codes are hashed, expire after 30 minutes and are single-use. Reset revokes all account sessions.
Portfolio sorting: name, city, last visit or next scheduled visit, ascending/descending; missing values always last. Next visit is a future appointment in the validated calendar, not a theoretical due date. Optional preferred time per account: start must fall within the profile tolerance (default ±30 minutes, range 0–240). Opening hours, lunch and return constraints remain active; accounts with no feasible slot remain unplanned. Existing routes are unchanged: regenerate simulations. No active XTR Conseil sender confirmed; local recovery remains available.
Staging 0.11: isolated configuration prepared, not deployed. See docs/PREPRODUCTION.md. Explicit HTTPS mode, mandatory demonstration database, registration disabled and individually provisioned tester accounts. Local mode and users remain unchanged.
Fixed appointments — version 0.12.0
In My calendar, double-click a day and select Add a fixed appointment. Enter a title, start, end and location (home or a secondary location saved in My account, using IGN/BAN address lookup). Add up to 24 time slots per day. Save the day and regenerate the simulation.
These appointments keep their exact times and locations: planning reserves travel time to reach them and leave afterwards. Account visits are scheduled around them. An impossible itinerary returns an error; fixed appointments are never moved. Slots must fall within the day’s working hours and cannot overlap each other or lunch; adjust lunch explicitly when needed. Non-field days remain excluded from routes.
Fixed appointments appear in ochre in the calendar even without a route, and are included in ICS exports and archives. They survive visit cancellation and restoring day defaults. To remove one, open the day, select Remove this time slot, and save. As with other daily settings, saving archives and clears that day’s route for recalculation; confirmed visits remain protected. Fixed appointments do not count as account visits or update last-visit dates.
Portfolio and company — version 0.13.0
The “Visit” filter offers “Visit”, “Do not visit” and “All”. It combines with search, town, priority and status. “Hide filters” saves space while keeping criteria and selection active; “Show filters” opens them again. My account includes an optional Company field, saved with personal identity without changing the calendar.
The test portfolio was expanded from the public RPPS extract dated 17 September 2026: 1,717 practice records, 1,542 practitioners and 127 towns in Haute-Garonne. Each record represents a practitioner and a practice structure; multiple practices produce multiple accounts. Coverage depends on the published source and is not a guarantee of real-world completeness. Existing accounts, notes and visit history are preserved. Personal records remain local and outside Git.
For this test dataset, the eastern sector means east of the Ariège, then east of the Garonne downstream of their confluence, without a radius limit. Located accounts in this sector are marked “Do not visit”. Existing exclusions elsewhere are preserved. Unresolved addresses are not geographically classified and need checking; street-level locations remain approximate. The exclusion checkbox remains editable in each account.
Places and calendar import — version 0.14.0
In My account → My places, use Add a location, enter a name (Office, South branch, Home 2…), search IGN/BAN and validate a suggestion to fill the complete address and coordinates. Save your settings. The location name appears in departure, return, lunch and fixed appointment selections.
For a fixed appointment, select a saved place or Other location. For a one-off place, enter its name and search and validate its address. It belongs only to this appointment and does not change your usual places. Routing uses its coordinates. Enter times as HH:MM (9:30 is also accepted and saved as 09:30). Errors appear inside the dialog: end after start, within the working day, with no overlap with lunch or another fixed appointment. Adjust that day’s lunch break if needed.
My calendar → Import an .ics calendar previews appointments before importing them as fixed commitments. Check places, select a saved or one-off location for each row, and remove unwanted rows. Import is atomic: a conflict blocks the whole batch without partial additions. Previously imported appointments with the same identity and start timestamp are ignored. Unconfirmed tours on affected days are archived then removed for recalculation; confirmed visits remain protected.
Current limits: 500 KB file, 200 appointments and 24 fixed slots per day. Dated occurrences with start and end on the same day and minute precision are converted to Europe/Paris. Floating times use Europe/Paris. Recurring series, all-day and overnight events are explicitly rejected: export individual occurrences. This is not a live Google, Apple or Outlook synchronization. Later source-calendar changes do not automatically update imported events.
Personal backup — version 0.15.0
My account → Back up and restore my data offers Export my data and Import a backup. All available categories are selected by default: settings/identity/places, accounts, calendar/leave/archives, completed visits/postponements, simulations and billing/subscription. Uncheck unwanted categories. Export downloads a Visit Manager JSON file; CSV remains for importing accounts only.
Import accepts a backup belonging to your current account, not another user’s backup. Choose a file and categories, then Check before restoring. The summary lists record counts by category. Tick the confirmation and restore. Selected categories are replaced, even when empty in the file; other categories are preserved. Categories missing from the file cannot be selected. Accounts with linked visits or plans require the related categories to be restored together; settings require calendar and simulations when those data exist. Inconsistent dependencies are rejected rather than breaking links.
A complete pre-restore recovery copy is retained locally in data/.user-backups with restricted access. It can be imported to undo the restoration. Restore is atomic and other users remain untouched. Passwords, sessions, reset codes and technical caches are excluded; the login email is unchanged. Files contain personal data: store downloads in a private location. Limit: 50 MB per file. The automatic local copy does not replace a downloaded backup kept off the computer.
Account owner — version 0.16.0
Each account has an optional commercial owner (150 characters), displayed in the list and included in search, CSV import through the owner column and JSON backups. This label grants no user access. In the Haute-Garonne test dataset, geolocated accounts west of the Ariège then Garonne default to Magalie Rousselin; an existing owner is preserved. Eastern accounts and those without coordinates are not assigned automatically. The RPPS preload uses the same rule.
Durable appointment history — version 0.17.0
Under History and visits, Past appointment history lets you browse a date range. It retains the name and address known at capture time, times, appointment type, status and visit report when available. Fixed appointments are included. Planned or confirmed appointments without a recorded outcome remain explicitly unreported; elapsed time never marks them completed.
A dedicated historical register is stored in the local database separately from the active calendar. It is updated at startup, on opening the workspace and around modifications. Previous appointment versions are retained; the list shows the latest known state. Clearing, cancelling or recalculating the calendar does not delete the register. Existing past appointments are recovered from remaining schedules, completed visits and archives; information already deleted without an archive cannot be reconstructed.
The register is private to each user and included in the Completed visits and postponements backup category. Version 2 JSON backups contain it; version 1 files remain accepted. An explicitly confirmed restoration of that category also replaces its history, with the usual pre-restore recovery copy.
Staging is available at https://visit-manager.fr as of 17 September 2026. OVH VPS deployment uses Caddy HTTPS, a loopback-only Python service, a separate demonstration database and disabled public registration. Individual credentials are shared privately; real portfolios require explicit owner authorization. Daily SQLite backups retain 14 copies on the VPS, in addition to OVH backup. Automatic email password recovery remains unconfigured.
Beta update: on the owner’s explicit request, a real portfolio may be transferred to their individual account. Require VISIT_STAGING_ACK=authorized-real-data and the matching staging_meta purpose=authorized-real-data marker; demo-only remains the default for fictional databases. Retain HTTPS, per-user isolation and closed registration. Back up before import and preserve account fields. The banner now states “Private beta”. Never commit secrets or data exports.
Presentation: the workspace header groups navigation on the left and language/account controls on the right. Explicit spacing, divider and a two-row mobile layout; French and English labels.
Version 0.18 — Appointments at the same address
Every simulation calculation or recalculation after rescheduling asks “Group appointments for accounts at the same address?”. Accept to share one standard duration from My account between eligible accounts with a zero custom duration. Decline to retain each full visit duration. Three standard accounts share 45 minutes as 15 each; two share it as 23 and 22 minutes, plus the full duration of any custom appointment. Minute rounding preserves the exact total. Custom durations stay individual even when equal to the standard duration.
Each appointment retains its own account, status and history. Only selected, visitable and available accounts participate. Opening hours, preferred times, lunch and fixed appointments remain respected; incompatible shared slots fall back to individual visits. Group size is limited to the slot’s number of minutes to prevent zero durations. The choice is stored with the simulation for saving and agenda transfer and is asked again on the next calculation.
Version 0.18.1 — Double-click appointment status
In the current calendar, double-click a visit and choose its status: Planned, Confirmed by account, Completed, Absent / reschedule or Cancelled. Click Save status. Completed opens actual duration and notes and is available from the appointment date. Absence opens rescheduling; cancellation requires confirmation. Errors remain visible in the dialog. Simulations and archives are read-only; finished visits retain their history.
Version 0.18.2 — Readable travel blocks
Travel blocks use the available day-column width instead of a 16-pixel strip. Start and end times come first, followed by duration and address when height permits. Very short trips keep a height proportional to their duration; hover or keyboard focus expands their details. Times and stored data are unchanged.
Version 0.18.3 — Saving locations
In My account, “Save my locations and settings” stays visible while scrolling, with another button at the bottom. Edits, adding a location and selecting an IGN address mark the form as unsaved. Leaving the section, following a same-tab link or signing out asks for confirmation: No keeps the draft, Yes discards it. Escape means No. Clicking My account again does not reset the form. Reloading or closing the page uses the browser’s native warning and wording. Protection clears after successful saving, remains after errors and preserves edits made during a save. It covers the locations and schedule form; other forms have separate save actions.
Version 0.19 — Automatic saving
My account locations and schedules are saved as a server-side draft one second after the last edit. Incomplete addresses are preserved without being validated; “Save my locations and settings” explicitly applies the form with the usual checks. The draft returns when My account is next opened. The indicator confirms automatic saving; on network failure, keep the page open and retry by editing a field. Closing before saving completes triggers the browser warning. Explicitly discarding removes the draft.
Every successful calculation automatically retains the latest simulation per user. It is restored on loading and available in Simulation calendar; no appointments are published. Review before transfer, especially after changing settings. Named simulations remain separately available. Drafts are included in full server backups, not in category-based JSON exports. User imports clear drafts to avoid stale references. Concurrent tabs use last-save-wins behavior; use one editing tab.
Version 0.20 — Account contact details
Account forms and portfolio rows show editable Business phone, Business mobile and Business email fields. The CSV template appends phone, mobile and professional_email without renaming existing columns. JSON backups preserve these fields.
Administrative enrichment: scripts/enrich_contacts.py uses Annuaire Santé RPPS (RPPS + practice identifier) and Ameli (full name + normalized street + postal code + town). Only empty fields are filled from unambiguous public values; published French 06/07 numbers are classified as business mobiles. Existing values remain unchanged and each addition records its source and verification date. No email guessing or MSSanté mailbox enrichment for business contacts. Preview is the default; --apply backs up SQLite and updates only the selected user without changing calendar, notes or owners. Source files and databases stay out of Git. Sources: https://www.data.gouv.fr/datasets/annuaire-sante-ameli and https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.21.0 — team licences and AI professional lists
The /licence page presents individual subscriptions and annual-only team packs: up to 5 users including the owner, proposed at EUR 699 including VAT; up to 10 at EUR 1,199 including VAT. Team prices require commercial approval before activation. For more than 10 users, contact XTR Conseil. Owners add or remove existing registered users. Workspaces stay separate and private. Seat limits are enforced on the server. Administrators edit prices and bilingual descriptions; zero hides an offer.
The /administration page is restricted to server-configured administrator emails. It covers users, blocking, free grants, password resets, estimated storage, workspace consultations, orders, invoices, prices and AI lists. A free grant does not issue a paid invoice. Manual payments require a receipt reference and explicit confirmation. PDF invoices are generated from order snapshots and retained. JSON invoice data prepare an accredited electronic invoicing platform connection; they do not perform regulatory transmission.
To prepare an AI list, enter a profession and area, choose a limit of 1 to 100, supply public source text as needed, build and edit the prompt, then save it. Under Providers, configure an exact model identifier and API key for OpenAI, Claude, DeepSeek or Gemini. Keys are encrypted on the server and never returned to the interface. Generation requires confirmation of a potentially billable call. OpenAI uses web search if supported by the model; the other three adapters analyse supplied text without web browsing in this version. Results may be empty and are never described as exhaustive or certified.
Results remain drafts for review. Open sources, check contact details, select rows and a target user workspace, then confirm import. Rows without a source cannot be imported. Matching name/address duplicates and repeated imports are avoided. AI-generated coordinates are discarded: validate addresses before route planning. No portfolio or calendar is automatically sent to an AI provider.
Delivery status: local development, adapters tested with simulated responses; no real AI call without keys. Stripe and no-reply@visit-manager.fr email require configuration and end-to-end verification. Consumer sales remain disabled. This preparatory version does not claim a deployed administration subdomain or commercial launch. Existing data are preserved. Grant appropriate access to beta testers before enabling licence enforcement.
Version 0.21.1 — official company search
Account creation/editing, My account billing details and licence checkout now provide dynamic French company search by name, SIREN or SIRET. Results show each establishment’s SIRET, address, status and head-office marker. Selecting a result fills the name, address, SIREN, SIRET, available VAT number, activity code and legal category. Review and save the form: selection alone does not save. Fields remain editable and manual entry remains available on service failure. The public Company Search API may omit non-public entities. No VAT number is calculated; an API-supplied number is not VIES validation. Only the search text is sent to the official service. Stale responses are discarded, requests are rate-limited and authentication is required. SIRET and VAT are preserved with account or billing records.
Licence presentation: smaller headings and prices, five offers aligned on wide screens, adaptive tablet and phone layout, and a compact centred sign-in form.
Version 0.22.0 — Home, purchase and contact
Paid plans and the trial appear on the home page using catalogue prices. The selected plan persists through registration or sign-in and billing details. The unchecked “I have read and accept the terms of sale” box is mandatory before Stripe; the terms version and acceptance timestamp are recorded. No payment starts without Stripe and approved commercial settings. Existing private-registration and consumer-sale gates remain in place.
The public contact form offers information, technical, licence/billing, improvement, privacy and a custom topic. Each request receives a VM-year-number reference; retrying the same submission does not duplicate it. Administrators can view the latest 200 requests and mark them new, in progress or closed. Notifications to xtr-conseil@orange.fr and acknowledgements are queued. The requested sender is no-reply.xtr-conseil@orange.fr, confirmed by the publisher to exist. SMTP still needs configuration; requests are recorded without sending email when it is unavailable.
Terms and privacy prohibit marketing or commercial exploitation, describe backups on OVHcloud infrastructure and recommend personal exports. They distinguish portfolio access through the management UI from possible technical server access; no end-to-end encryption guarantee is made. Communications are limited to the service, contract and replies to user requests.
Version 0.22.1 — Contact history
Administration retains received messages and a dated history of actions and their author. Track case status (New / In progress / Closed) separately from response status (Awaiting response / Answered / No response needed). An internal note is required when marking a request answered or unnecessary. It records a response sent separately or a reason; saving sends no email. Automatic acknowledgement is not a response. Original messages and earlier notes remain unchanged.
Search by reference, name, email or subject and filter by case and response status. Requests load in pages of 50, including older records. Existing cases retain their status and receive a history baseline without inventing earlier responses. Revision checks reject stale concurrent updates.
Mentions légales / Legal notice
Les mentions légales XTR Conseil sont accessibles depuis les pages du site, en français et en anglais, sans connexion. Voir le dossier de mentions (docs/MENTIONS_LEGALES.md). Les CGV, la confidentialité propre au service et les accès existants sont conservés.
The XTR Conseil legal notice is publicly accessible from site pages in French and English. See legal notice documentation (docs/MENTIONS_LEGALES.md). Existing terms, service-specific privacy information and access controls are preserved.
Version 0.22.2 — Administration security
The Security tab changes the signed-in administrator’s own password: current password, new password (12–256 characters) and confirmation. Verification is limited to five attempts per fifteen minutes. Passwords are hashed and excluded from audit records. Success invalidates every session and reset token for this account; signing in again is required. Other users remain unchanged.
Support is prepared for admin.visit-manager.fr: configure VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, add DNS and the Caddy site in deploy/staging/admin.Caddyfile.example, then verify HTTPS. Preserve the subdomain Host header. Origins are validated separately per host and cookies remain host-only. Non-administrative APIs are rejected on the subdomain. Activated on 18 September 2026: OVH DNS, HTTPS certificate and administrator access verified. The existing /administration path remains available.
The portfolio uses an aligned action bar, multi-column filters and a more compact list. On phones, controls reflow and the table retains horizontal scrolling. Hide/Show filters remains available.
CSV template downloads headers in the active language, with a fictional second row to replace or delete. Template formats opens per-column guidance (YYYY-MM-DD dates, HH:MM times, durations in minutes, codes and phone numbers imported as text). Imports accept French, English and historical technical headers regardless of interface language. Decimal commas are accepted for coordinates. Duplicate headers and malformed rows are rejected before insertion.
User creation and temporary access (12 hours)
The administrator creates an account, selects its language and receives a temporary password displayed once. They can share it directly or use “Send temporary password”: this generates a fresh secret, invalidates the previous one and emails instructions. The user must choose a different password on first sign-in before accessing any data. After 12 hours, issue a new invitation. Passwords are not stored in clear text in logs or mail queues. Check the SMTP outcome; failed or uncertain delivery is reported. Creation does not send email automatically.
Version 0.23.0 — Temporary access and portfolio
Administrator-created access expires after 12 hours and requires password replacement; explicit localized email delivery, bilingual CSV templates and compact portfolio layout. Existing accounts, settings and calendars are preserved.
Technical documentation
Presentation: the workspace header groups navigation on the left and language/account controls on the right. Explicit spacing, divider and a two-row mobile layout; French and English labels.
Beta update: on the owner’s explicit request, a real portfolio may be transferred to their individual account. Require VISIT_STAGING_ACK=authorized-real-data and the matching staging_meta purpose=authorized-real-data marker; demo-only remains the default for fictional databases. Retain HTTPS, per-user isolation and closed registration. Back up before import and preserve account fields. The banner now states “Private beta”. Never commit secrets or data exports.
Staging is available at https://visit-manager.fr as of 17 September 2026. OVH VPS deployment uses Caddy HTTPS, a loopback-only Python service, a separate demonstration database and disabled public registration. Individual credentials are shared privately; real data requires explicit authorization. Daily SQLite backups retain 14 copies on the VPS, in addition to OVH backup. Automatic email password recovery remains unconfigured.
Version 0.17: appointment_journal is an append-only per-user register with event_key, day, capture time, fingerprint and snapshot JSON. appointment_journal.py collects archives, current schedules and cancellations; recorded completions take precedence. Capture only past appointments (Europe/Paris), except completions declared today. Capture never removes the active calendar. Insert a version only when the latest state differs, retaining previous states. Sync at startup, on /api/state and before/after authenticated writes, excluding backup preview and geocoding. The per-user list returns each event’s latest state; UI date filters apply. Include the register in v2 backup history, remap client_id/event_key during restore and accept v1 without the register. Test retention after calendar removal, deduplication, archive backfill, completion and isolation.
Version 0.16: optional clients.data.owner, validated at 150 characters, preserved on edits and in backups. Display and search owner; append CSV owner without renaming existing columns. RPPS preload: if eastern_sector is exactly False and owner is empty, assign Magalie Rousselin. Do not confuse missing coordinates with West, or commercial ownership with user access.
Version 0.15.0
user_backup.py defines a versioned JSON format and six allowlisted table groups, excluding authentication credentials. A SHA-256 digest detects accidental section changes (not a signature); an owner account digest restricts restoration to that account. POST /api/backup-export uses a transactional read. /api/backup-preview restores under a SAVEPOINT then rolls back; /api/backup-import requires confirmed=true and writes a private recovery copy before final completion. Column allowlists, dependency checks, account reference remapping on ID collisions, regenerated global row IDs, location checks and private-reference validation apply. All operations require session/CSRF/Origin. Requests allow 51 MB only for preview/restore; exports are limited to 50 MB. Recovery files: data/.user-backups, mode 0600. UI: web/backup.js; tests/test_backup.py covers isolation, integrity, confirmation, preview, replacement, collisions and rollback.
Version 0.14.0
Profile places retain name, structured fields and coordinates. fixed_appointments accepts either a profile location_id or an embedded location validated with profile address rules. fixed_events uses this location for routing and exports. source_uid identifies an imported ICS occurrence. calendar_import.py provides a bounded parser (line unfolding, escapes, UTC/TZID/Europe/Paris, rejection of recurring series and all-day events). POST /api/calendar-preview returns occurrences; POST /api/calendar-import validates all days before writing, protects confirmed visits, archives and invalidates affected tours, and deduplicates without changing untouched days. Both endpoints require authentication, CSRF and user isolation. Tests: tests/test_calendar_import.py.
Version 0.13.0
Additive migration user_identity.company TEXT NOT NULL DEFAULT ''. /api/identity accepts 200 characters, preserves company when omitted, and isolates by user_id. No effect on tours. Visit and visibility filters run in the browser; hiding filters does not reset criteria.
scripts/import_rpps_department.py prepares RPPS rows (profession 40, department 31 commune), deduplicates by RPPS + structure ID and preloads only the requested user with SQLite backup, transaction and preservation of existing records. --raw accepts a JSON array extracted from the official source; --records identifies the prepared file. Example: python3 scripts/import_rpps_department.py --raw /tmp/rpps31.json --date 2026-09-17 --records /tmp/records.json. Enrich coordinates through IGN with matching commune/street/number before --records /tmp/records.json --db data/visit.sqlite --user-id ID --apply. Import does not geocode automatically. Missing addresses remain unresolved; never fabricate coordinates. eastern_sector uses the connected Ariège then Garonne trace (resources/ariege-garonne.geojson), closed eastward outside the department, and a point-in-polygon test. Source: OpenStreetMap relations 1104538 and 70423, © OpenStreetMap contributors, ODbL 1.0. Keep datasets and backups outside Git. Tests: tests/test_department.py, tests/test_identity.py and tests/portfolio_filters.js.
Accounts store optional HH:MM preferred_time; profiles store preferred_time_tolerance from 0 to 240, default 30. Planning checks the start window both before and after lunch. JS sorting uses locale, ID tie-break and missing values last.
server.py provides the JSON API, allowlisted static files, SQLite transactions and authentication. Default database: data/visit.sqlite; VISIT_DB and VISIT_PORT configure database and port. Start using python3 server.py. Python 3.10+ and no third-party server dependency. Local-only operation; use a consistent SQLite backup for live data.
Data covers user_identity (first/last name per user, /api/identity), users/sessions/attempts, user-scoped accounts, completed visits, scenarios, profiles, daily overrides, calendar routes, leave, archives, appointment history and billing profiles. Schema and migrations in server.py are authoritative. Private databases are excluded from Git.
Modules: planner.py for greedy planning; routing.py for IGN/cache; geocoding.py for address normalization; agenda.py for preferences/context/ICS; working_calendar.py for working days; appointments.py for status/cancellation; agenda_history.py for snapshots/merging; commerce.py for publisher/version/billing preparation; translations.py for errors. UI uses app.js, agenda.js, lifecycle.js, i18n.js, messages.json and local CSS/logo.
The generated endpoint appendix lists API paths. Writes require session, CSRF and Origin validation; private resources are user-scoped. Passwords use scrypt, sessions expire after 12 hours, cookies use HttpOnly/SameSite and login is throttled. Production SaaS, TLS operations, monitoring and payment configuration remain outside the current local release. Invoice previews do not issue paid invoices.
Zero or missing account duration uses profile visit_duration; explicit durations range from 5 to 480 minutes. Legacy profiles use 45 minutes. Published appointments retain their durations. Existing profile-save protections and archive behavior remain active.
Release checks: unittest discovery, syntax-check every web JavaScript file, generate documentation and run scripts/build_docs.py --check. The generator includes branded bilingual portals, four documentation sections, endpoint inventory and SHA-256 source manifest. Hash freshness does not replace editorial review. Restore exact code from Git and private data from a separate backup.
Password recovery / Récupération : password_reset.py EMAIL génère localement un jeton 256 bits. SHA-256 en table password_resets, expiration 30 min, consommation atomique DELETE RETURNING, révocation sessions. /api/password-forgot répond uniformément et enregistre password_reset_requests ; /api/password-reset valide code et longueur. Limite 10 requêtes/15 min/IP. No SMTP; local administrator must verify identity and deliver the code privately.
Préproduction / Staging : deployment.py contrôle mode et isolation ; scripts/create_demo_tester.py initialise la base marquée staging_meta avec utilisateurs uniques et 12 comptes fictifs chacun. Voir le guide de préproduction pour la configuration systemd/Caddy et les contrôles externes requis.
FR — Créneaux fixes (0.12.0)
Stocker fixed_appointments dans les overrides agenda_days isolés par utilisateur : title, start, end, location_id. Valider les lieux du profil, la durée positive, les horaires de journée et les collisions avec créneaux/déjeuner. Le moteur glouton vérifie la possibilité de rejoindre la prochaine ancre avant d’accepter un compte ; il avance à l’ancre quand aucun compte ne tient dans l’intervalle. Les trajets utilisent le routeur existant, y compris son repli estimé. Le merge/recalcul traite les ancres dans l’ordre chronologique, sans les convertir en visites. L’export reconstruit les événements fixes depuis les overrides, même sans agenda_routes. Aucune migration destructive. Tests : multiples ancres, trajets impossibles, collisions, export, isolation, CSRF, persistance et suppression explicite.
EN — Fixed slots (0.12.0)
Store user-scoped fixed_appointments in agenda_days overrides: title, start, end, location_id. Validate profile locations, positive duration, day boundaries and slot/lunch overlap. Before accepting a visit, the greedy planner checks that the next anchor remains reachable, advancing to it when no visit fits. Travel uses the existing router and estimated fallback. Merge/rebuild processes anchors chronologically without turning them into account visits. Export reconstructs fixed events from overrides even without agenda_routes. No destructive migration. Tests cover multiple anchors, unreachable locations, collisions, export, isolation, CSRF, persistence and explicit removal.
Version 0.18 — Rendez-vous à la même adresse
À chaque calcul de simulation ou recalcul après report, répondez à « Grouper les rendez-vous des comptes à la même adresse ? ». Accepter partage une seule durée standard de Mon compte entre les comptes éligibles dont la durée spécifique est zéro. Refuser conserve la durée complète de chaque visite. Trois comptes standards de 45 minutes prennent chacun 15 minutes ; deux standards prennent 23 et 22 minutes, plus la durée intégrale de tout compte personnalisé. L’arrondi à la minute conserve exactement le total. Une durée personnalisée reste individuelle même si elle est égale à la durée standard.
Les rendez-vous gardent leur compte, leur statut et leur historique individuels. Seuls les comptes sélectionnés, à visiter et disponibles sont inclus. Les heures d’ouverture, heures préférentielles, déjeuner et rendez-vous fixes restent respectés ; si le créneau commun est incompatible, les visites restent individuelles. Les groupes sont limités au nombre de minutes du créneau pour éviter une durée nulle. La réponse est conservée avec la simulation pour son enregistrement et son transfert à l’agenda, puis redemandée au prochain calcul.
Version 0.18 — Appointments at the same address
Every simulation calculation or recalculation after rescheduling asks “Group appointments for accounts at the same address?”. Accept to share one standard duration from My account between eligible accounts with a zero custom duration. Decline to retain each full visit duration. Three standard accounts share 45 minutes as 15 each; two share it as 23 and 22 minutes, plus the full duration of any custom appointment. Minute rounding preserves the exact total. Custom durations stay individual even when equal to the standard duration.
Each appointment retains its own account, status and history. Only selected, visitable and available accounts participate. Opening hours, preferred times, lunch and fixed appointments remain respected; incompatible shared slots fall back to individual visits. Group size is limited to the slot’s number of minutes to prevent zero durations. The choice is stored with the simulation for saving and agenda transfer and is asked again on the next calculation.
Version 0.18.1 — Statut par double-clic
Dans l’agenda actuel, double-cliquez sur une visite puis choisissez son statut dans la liste : Planifié, Confirmé par le compte, Réalisé, Absent / à reprogrammer ou Annulé. Cliquez sur Enregistrer le statut. Réalisé ouvre la saisie de durée réelle et compte rendu et n’est disponible qu’à partir du jour du rendez-vous. Une absence ouvre la reprogrammation et une annulation demande confirmation. Les erreurs restent visibles dans la fiche. Simulations et archives restent en consultation ; les visites terminées conservent leur historique.
Version 0.18.1 — Double-click appointment status
In the current calendar, double-click a visit and choose its status: Planned, Confirmed by account, Completed, Absent / reschedule or Cancelled. Click Save status. Completed opens actual duration and notes and is available from the appointment date. Absence opens rescheduling; cancellation requires confirmation. Errors remain visible in the dialog. Simulations and archives are read-only; finished visits retain their history.
Version 0.18.2 — Lisibilité des trajets
Les trajets occupent la largeur disponible dans la colonne du jour, au lieu d’une bande de 16 pixels. Leurs heures de début et de fin apparaissent en premier, puis leur durée et leur adresse selon la hauteur disponible. Un trajet très court garde une hauteur proportionnelle à sa durée ; le survol ou le focus développe son détail. Les horaires et les données ne changent pas.
Version 0.18.2 — Readable travel blocks
Travel blocks use the available day-column width instead of a 16-pixel strip. Start and end times come first, followed by duration and address when height permits. Very short trips keep a height proportional to their duration; hover or keyboard focus expands their details. Times and stored data are unchanged.
Version 0.18.3 — Sauvegarde des lieux
Dans Mon compte, « Sauvegarder mes lieux et paramètres » reste visible pendant le défilement ; un second bouton est disponible en bas. La saisie, l’ajout de lieu et le choix d’adresse IGN sont suivis comme modifications non sauvegardées. Quitter la rubrique, suivre un lien dans le même onglet ou se déconnecter demande confirmation : Non conserve la saisie, Oui abandonne. Échap équivaut à Non. Un nouvel appui sur Mon compte ne réinitialise pas le formulaire. Actualisation/fermeture : avertissement natif du navigateur, avec son propre texte. La protection disparaît après une sauvegarde réussie, reste après erreur et protège les changements effectués pendant un enregistrement. Elle concerne le formulaire des lieux et horaires ; les autres formulaires ont leur propre enregistrement.
Version 0.18.3 — Saving locations
In My account, “Save my locations and settings” stays visible while scrolling, with another button at the bottom. Edits, adding a location and selecting an IGN address mark the form as unsaved. Leaving the section, following a same-tab link or signing out asks for confirmation: No keeps the draft, Yes discards it. Escape means No. Clicking My account again does not reset the form. Reloading or closing the page uses the browser’s native warning and wording. Protection clears after successful saving, remains after errors and preserves edits made during a save. It covers the locations and schedule form; other forms have separate save actions.
Version 0.19 — Sauvegarde automatique
Les lieux et horaires de Mon compte sont sauvegardés en brouillon sur le serveur une seconde après la dernière modification. Une adresse incomplète est conservée sans être validée ; « Sauvegarder mes lieux et paramètres » applique explicitement le formulaire et conserve les contrôles habituels. Le brouillon revient à la prochaine ouverture de Mon compte. L’indicateur confirme la sauvegarde automatique ; en cas d’erreur réseau, gardez la page ouverte et réessayez en modifiant un champ. Une fermeture avant la fin de la sauvegarde déclenche l’avertissement du navigateur. Abandonner explicitement supprime le brouillon.
Chaque calcul réussi conserve automatiquement la dernière simulation par utilisateur. Elle est restaurée au chargement et consultable dans Agenda de simulation ; elle ne publie aucun rendez-vous. Vérifiez-la avant transfert, notamment après changement de paramètres. Les simulations nommées restent disponibles séparément. Les brouillons sont inclus dans les sauvegardes complètes du serveur, pas dans les exports JSON par catégorie. Un import utilisateur efface les brouillons pour éviter des références obsolètes. Deux onglets modifiant le même brouillon suivent la règle du dernier enregistrement ; utilisez un seul onglet de saisie.
Version 0.19 — Automatic saving
My account locations and schedules are saved as a server-side draft one second after the last edit. Incomplete addresses are preserved without being validated; “Save my locations and settings” explicitly applies the form with the usual checks. The draft returns when My account is next opened. The indicator confirms automatic saving; on network failure, keep the page open and retry by editing a field. Closing before saving completes triggers the browser warning. Explicitly discarding removes the draft.
Every successful calculation automatically retains the latest simulation per user. It is restored on loading and available in Simulation calendar; no appointments are published. Review before transfer, especially after changing settings. Named simulations remain separately available. Drafts are included in full server backups, not in category-based JSON exports. User imports clear drafts to avoid stale references. Concurrent tabs use last-save-wins behavior; use one editing tab.
Version 0.20 — Coordonnées des comptes
Les fiches et la liste du portefeuille affichent Téléphone professionnel, Mobile professionnel et E-mail professionnel, modifiables par compte. Le modèle CSV ajoute phone, mobile et professional_email sans renommer les colonnes existantes. Les sauvegardes JSON conservent ces champs.
Enrichissement administratif : scripts/enrich_contacts.py utilise l’Annuaire Santé RPPS (identifiant RPPS + structure) et Ameli (nom complet + voie normalisée + code postal + commune). Il complète uniquement les champs vides à partir de valeurs publiques non ambiguës, classe les numéros français 06/07 comme mobiles professionnels publiés, conserve les saisies et trace la source et la date de vérification par champ. Aucun e-mail déduit, aucune messagerie MSSanté ajoutée pour le contact commercial. Mode prévisualisation par défaut ; --apply crée une sauvegarde SQLite puis met à jour uniquement l’utilisateur sélectionné, sans modifier son agenda, ses notes ou ses propriétaires. Fichiers sources et bases privés, exclus de Git. Sources : https://www.data.gouv.fr/datasets/annuaire-sante-ameli et https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.20 — Account contact details
Account forms and portfolio rows show editable Business phone, Business mobile and Business email fields. The CSV template appends phone, mobile and professional_email without renaming existing columns. JSON backups preserve these fields.
Administrative enrichment: scripts/enrich_contacts.py uses Annuaire Santé RPPS (RPPS + practice identifier) and Ameli (full name + normalized street + postal code + town). Only empty fields are filled from unambiguous public values; published French 06/07 numbers are classified as business mobiles. Existing values remain unchanged and each addition records its source and verification date. No email guessing or MSSanté mailbox enrichment for business contacts. Preview is the default; --apply backs up SQLite and updates only the selected user without changing calendar, notes or owners. Source files and databases stay out of Git. Sources: https://www.data.gouv.fr/datasets/annuaire-sante-ameli and https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.21.0 — team licences and AI professional lists
The /licence page presents individual subscriptions and annual-only team packs: up to 5 users including the owner, proposed at EUR 699 including VAT; up to 10 at EUR 1,199 including VAT. Team prices require commercial approval before activation. For more than 10 users, contact XTR Conseil. Owners add or remove existing registered users. Workspaces stay separate and private. Seat limits are enforced on the server. Administrators edit prices and bilingual descriptions; zero hides an offer.
The /administration page is restricted to server-configured administrator emails. It covers users, blocking, free grants, password resets, estimated storage, workspace consultations, orders, invoices, prices and AI lists. A free grant does not issue a paid invoice. Manual payments require a receipt reference and explicit confirmation. PDF invoices are generated from order snapshots and retained. JSON invoice data prepare an accredited electronic invoicing platform connection; they do not perform regulatory transmission.
To prepare an AI list, enter a profession and area, choose a limit of 1 to 100, supply public source text as needed, build and edit the prompt, then save it. Under Providers, configure an exact model identifier and API key for OpenAI, Claude, DeepSeek or Gemini. Keys are encrypted on the server and never returned to the interface. Generation requires confirmation of a potentially billable call. OpenAI uses web search if supported by the model; the other three adapters analyse supplied text without web browsing in this version. Results may be empty and are never described as exhaustive or certified.
Results remain drafts for review. Open sources, check contact details, select rows and a target user workspace, then confirm import. Rows without a source cannot be imported. Matching name/address duplicates and repeated imports are avoided. AI-generated coordinates are discarded: validate addresses before route planning. No portfolio or calendar is automatically sent to an AI provider.
Delivery status: local development, adapters tested with simulated responses; no real AI call without keys. Stripe and no-reply@visit-manager.fr email require configuration and end-to-end verification. Consumer sales remain disabled. This preparatory version does not claim a deployed administration subdomain or commercial launch. Existing data are preserved. Grant appropriate access to beta testers before enabling licence enforcement.
Version 0.21.1 — official company search
Account creation/editing, My account billing details and licence checkout now provide dynamic French company search by name, SIREN or SIRET. Results show each establishment’s SIRET, address, status and head-office marker. Selecting a result fills the name, address, SIREN, SIRET, available VAT number, activity code and legal category. Review and save the form: selection alone does not save. Fields remain editable and manual entry remains available on service failure. The public Company Search API may omit non-public entities. No VAT number is calculated; an API-supplied number is not VIES validation. Only the search text is sent to the official service. Stale responses are discarded, requests are rate-limited and authentication is required. SIRET and VAT are preserved with account or billing records.
Licence presentation: smaller headings and prices, five offers aligned on wide screens, adaptive tablet and phone layout, and a compact centred sign-in form.
Version 0.22.0 — Accueil, souscription et contact
Les offres payantes et l’essai sont présentés sur l’accueil, avec prix provenant du catalogue. Le choix est conservé dans le parcours de création de compte ou de connexion, puis dans les coordonnées de facturation. La case « J’ai lu les conditions générales de vente et je les accepte » est obligatoire avant Stripe ; sa version et la date d’acceptation sont enregistrées. Aucun paiement n’est lancé lorsque Stripe ou les autorisations commerciales ne sont pas configurés. Les inscriptions privées et les ventes aux particuliers restent protégées par leurs verrous existants.
Le formulaire public Contact propose information, technique, licence/facturation, suggestion, confidentialité et autre thème libre. Chaque demande reçoit une référence VM-année-numéro ; les répétitions du même envoi ne créent pas de doublon. L’administration affiche les 200 dernières demandes et permet les statuts nouvelle/en cours/clôturée. Les messages destinés à xtr-conseil@orange.fr et les accusés de réception sont conservés dans la file d’envoi. L’expéditeur souhaité est no-reply.xtr-conseil@orange.fr, confirmé existant par l’éditeur. Sa configuration SMTP reste nécessaire ; sans elle, l’enregistrement reste possible mais aucun e-mail n’est envoyé.
Les CGV et la confidentialité précisent l’absence de prospection ou d’exploitation commerciale, les sauvegardes sur l’infrastructure OVHcloud et la recommandation d’exports personnels. Elles distinguent l’absence de consultation des portefeuilles dans l’administration et la possibilité d’accès technique au serveur : aucune garantie de chiffrement de bout en bout n’est annoncée. Communications limitées au service, au contrat et aux réponses aux sollicitations.
Version 0.22.0 — Home, purchase and contact
Paid plans and the trial appear on the home page using catalogue prices. The selected plan persists through registration or sign-in and billing details. The unchecked “I have read and accept the terms of sale” box is mandatory before Stripe; the terms version and acceptance timestamp are recorded. No payment starts without Stripe and approved commercial settings. Existing private-registration and consumer-sale gates remain in place.
The public contact form offers information, technical, licence/billing, improvement, privacy and a custom topic. Each request receives a VM-year-number reference; retrying the same submission does not duplicate it. Administrators can view the latest 200 requests and mark them new, in progress or closed. Notifications to xtr-conseil@orange.fr and acknowledgements are queued. The requested sender is no-reply.xtr-conseil@orange.fr, confirmed by the publisher to exist. SMTP still needs configuration; requests are recorded without sending email when it is unavailable.
Terms and privacy prohibit marketing or commercial exploitation, describe backups on OVHcloud infrastructure and recommend personal exports. They distinguish portfolio access through the management UI from possible technical server access; no end-to-end encryption guarantee is made. Communications are limited to the service, contract and replies to user requests.
Version 0.22.1 — Historique des demandes
L’administration conserve les messages reçus avec un historique daté des actions et de leur auteur. Deux suivis distincts : dossier Nouveau / En cours / Clôturé ; réponse À répondre / Répondu / Réponse non nécessaire. Une note interne est obligatoire lors du passage à Répondu ou Réponse non nécessaire. Elle décrit la réponse effectuée ailleurs ou le motif ; l’enregistrement ne transmet aucun e-mail. L’accusé automatique ne vaut pas réponse. Les messages originaux et les anciennes notes ne sont pas écrasés.
Recherche par référence, nom, e-mail ou objet ; filtres par statut et réponse. Les demandes sont chargées par pages de 50, avec accès aux plus anciennes. Les demandes existantes gardent leur statut et reçoivent un point de départ d’historique, sans inventer de réponse passée. Un contrôle de révision refuse les modifications concurrentes devenues obsolètes.
Version 0.22.1 — Contact history
Administration retains received messages and a dated history of actions and their author. Track case status (New / In progress / Closed) separately from response status (Awaiting response / Answered / No response needed). An internal note is required when marking a request answered or unnecessary. It records a response sent separately or a reason; saving sends no email. Automatic acknowledgement is not a response. Original messages and earlier notes remain unchanged.
Search by reference, name, email or subject and filter by case and response status. Requests load in pages of 50, including older records. Existing cases retain their status and receive a history baseline without inventing earlier responses. Revision checks reject stale concurrent updates.
Mentions légales / Legal notice (18/09/2026) : préserver la page bilingue mentions-legales.html, legal.css et les liens depuis les pages publiques ; éditeur XTR Conseil et hébergeur OVH pour les instances hébergées. Keep the bilingual public legal notice and links, separately from service-specific terms and privacy. See docs/MENTIONS_LEGALES.md.
Version 0.22.2 — Sécurité de l’administration
L’onglet Sécurité permet de changer son propre mot de passe : mot de passe actuel, nouveau mot de passe (12 à 256 caractères), confirmation. Seul un administrateur connecté peut effectuer cette action. Cinq tentatives de vérification maximum sur quinze minutes ; les erreurs sont traduites. Les mots de passe sont hachés et ne figurent pas dans l’historique. Toutes les sessions et les codes de réinitialisation de ce compte sont invalidés après succès ; une nouvelle connexion est obligatoire. Les autres utilisateurs ne sont pas modifiés.
Prise en charge préparée pour admin.visit-manager.fr : définir VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, ajouter son DNS et le site Caddy fourni dans deploy/staging/admin.Caddyfile.example, puis vérifier HTTPS. Préserver l’en-tête Host du sous-domaine. Les origines sont vérifiées séparément pour chaque hôte ; les cookies restent propres à chaque hôte. L’API du sous-domaine refuse les accès non administratifs. Sous-domaine activé le 18 septembre 2026 : DNS OVH, certificat HTTPS et accès administrateur vérifiés. L’administration existante conserve son chemin /administration.
Version 0.22.2 — Administration security
The Security tab changes the signed-in administrator’s own password: current password, new password (12–256 characters) and confirmation. Verification is limited to five attempts per fifteen minutes. Passwords are hashed and excluded from audit records. Success invalidates every session and reset token for this account; signing in again is required. Other users remain unchanged.
Support is prepared for admin.visit-manager.fr: configure VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, add DNS and the Caddy site in deploy/staging/admin.Caddyfile.example, then verify HTTPS. Preserve the subdomain Host header. Origins are validated separately per host and cookies remain host-only. Non-administrative APIs are rejected on the subdomain. Activated on 18 September 2026: OVH DNS, HTTPS certificate and administrator access verified. The existing /administration path remains available.
Portefeuille compact / Compact portfolio
Le portefeuille adopte une barre d’actions alignée, des filtres répartis sur plusieurs colonnes et une liste plus compacte. Sur téléphone, les commandes se réorganisent ; le tableau conserve son défilement horizontal. Les boutons Masquer/Afficher les filtres restent disponibles.
The portfolio uses an aligned action bar, multi-column filters and a more compact list. On phones, controls reflow and the table retains horizontal scrolling. Hide/Show filters remains available.
CSS limited to #clients: account-tools, account-filter-card, account-table. Preserve IDs, event handlers, translations, native file upload and existing account data. Breakpoints: 1100, 700 and 420 px. No server or database changes.
Modèles CSV localisés / Localized CSV templates
Le bouton Modèle CSV télécharge les en-têtes dans la langue active, avec une deuxième ligne fictive à remplacer ou supprimer. Formats du modèle ouvre l’aide de chaque colonne (dates AAAA-MM-JJ, heures HH:MM, durées en minutes, codes et téléphones à importer comme texte). L’import accepte les en-têtes français, anglais et techniques historiques, indépendamment de la langue affichée. Les virgules décimales sont acceptées pour les coordonnées. Les doublons d’en-têtes et les lignes mal formées sont rejetés avant ajout.
CSV template downloads headers in the active language, with a fictional second row to replace or delete. Template formats opens per-column guidance (YYYY-MM-DD dates, HH:MM times, durations in minutes, codes and phone numbers imported as text). Imports accept French, English and historical technical headers regardless of interface language. Decimal commas are accepted for coordinates. Duplicate headers and malformed rows are rejected before insertion.
Schema: web/csv-columns.json (keys, FR/EN headers, examples and format guidance). csv_accounts.read_accounts normalizes header accents/case/spacing and retains historical metadata. User-entered text remains unchanged.
Création d’utilisateur et accès temporaire (12 heures)
L’administrateur crée le compte, choisit sa langue et reçoit un mot de passe temporaire affiché une seule fois. Il peut le transmettre directement ou utiliser « Envoyer un mot de passe temporaire » : ce bouton génère un nouveau secret, invalide le précédent et envoie les instructions par e-mail. L’utilisateur doit choisir un mot de passe différent dès la première connexion, avant tout accès aux données. Après 12 heures, une nouvelle invitation est nécessaire. Aucun mot de passe en clair n’est conservé dans les journaux ni les files d’envoi. Vérifier le résultat SMTP ; un échec ou résultat incertain est signalé. Aucun envoi automatique à la création.
User creation and temporary access (12 hours)
The administrator creates an account, selects its language and receives a temporary password displayed once. They can share it directly or use “Send temporary password”: this generates a fresh secret, invalidates the previous one and emails instructions. The user must choose a different password on first sign-in before accessing any data. After 12 hours, issue a new invitation. Passwords are not stored in clear text in logs or mail queues. Check the SMTP outcome; failed or uncertain delivery is reported. Creation does not send email automatically.
Languages: Visit Manager FR/EN; AO Manager FR; Expertise Manager FR; Dental Prep fr, en-GB, de, es, it, pt-PT, sv, da, nl-NL, nl-BE, ar. Excludes mapsychopraticienne.fr/.com. Visit Manager: temporary_access table, admin-invitation endpoint, SHA-256 hash of a random high-entropy secret, one-time 15-minute reset challenge bounded by the original 12-hour expiry; no business session before reset. Regeneration deletes earlier reset tokens and sessions. Existing accounts and role checks remain unchanged.
Regeneration prompt
Presentation: the workspace header groups navigation on the left and language/account controls on the right. Explicit spacing, divider and a two-row mobile layout; French and English labels.
Beta update: on the owner’s explicit request, a real portfolio may be transferred to their individual account. Require VISIT_STAGING_ACK=authorized-real-data and the matching staging_meta purpose=authorized-real-data marker; demo-only remains the default for fictional databases. Retain HTTPS, per-user isolation and closed registration. Back up before import and preserve account fields. The banner now states “Private beta”. Never commit secrets or data exports.
Staging is available at https://visit-manager.fr as of 17 September 2026. OVH VPS deployment uses Caddy HTTPS, a loopback-only Python service, a separate demonstration database and disabled public registration. Individual credentials are shared privately; real data requires explicit authorization. Daily SQLite backups retain 14 copies on the VPS, in addition to OVH backup. Automatic email password recovery remains unconfigured.
Version 0.17: appointment_journal is an append-only per-user register with event_key, day, capture time, fingerprint and snapshot JSON. appointment_journal.py collects archives, current schedules and cancellations; recorded completions take precedence. Capture only past appointments (Europe/Paris), except completions declared today. Capture never removes the active calendar. Insert a version only when the latest state differs, retaining previous states. Sync at startup, on /api/state and before/after authenticated writes, excluding backup preview and geocoding. The per-user list returns each event’s latest state; UI date filters apply. Include the register in v2 backup history, remap client_id/event_key during restore and accept v1 without the register. Test retention after calendar removal, deduplication, archive backfill, completion and isolation.
Version 0.16: optional clients.data.owner, validated at 150 characters, preserved on edits and in backups. Display and search owner; append CSV owner without renaming existing columns. RPPS preload: if eastern_sector is exactly False and owner is empty, assign Magalie Rousselin. Do not confuse missing coordinates with West, or commercial ownership with user access.
Version 0.15: add personal-space JSON backup/restore with six categories (settings, accounts, calendar, history, simulations, billing), all selected by default. Preview available categories before explicit replacement confirmation. Preserve unselected categories and reject inconsistent dependencies. Create a private recovery copy first, restore atomically, remap account references, reject another user’s backup, exclude secrets and sessions. Follow limits and checks in TECHNIQUE.md and backup tests.
Version 0.14: keep named places with full addresses in My account. Allow an embedded one-off location on fixed appointments without adding it to the profile. Use HH:MM times with errors inside the dialog. Add preview followed by atomic ICS import, location selection/validation, occurrence deduplication, confirmed-visit protection and required tour recalculation. Follow documented size limits and timezone rules; do not silently discard unsupported events.
Add optional company to identity (200 characters), a three-value Visit filter and collapsible filters preserving criteria. Provide an idempotent, per-user administrative RPPS Haute-Garonne import with backup and preservation of notes/history. Define the test eastern sector as in TECHNIQUE.md; do not classify missing coordinates. Do not distribute personal records in the repository.
Add ascending/descending portfolio sorting by name, city, last/next scheduled visit, missing values last. Respect optional account preferred time with 0–240 minute profile tolerance, including after inserting lunch.
Rebuild Visit Manager for XTR Conseil using all supplied functional and technical specifications. Deliver a locally runnable Python-standard-library/SQLite/HTML/CSS/JavaScript project with bilingual UI, documentation, source and tests. Read README_EN.md, AGENTS.md, TECHNIQUE.md and all tests. Exact generated code identity is not guaranteed; use the corresponding Git commit for an exact restoration.
Include a user-scoped first/last name identity and top-right initials opening My account; identity edits preserve planning. Explain duplicate registration and prompt sign-in. Implement isolated individual accounts, scrypt passwords, hashed 12-hour sessions, CSRF, Host/Origin checks, login throttling and commit-before-success semantics. Keep the server local. Provide landing page, filtered portfolio, CSV import, double-click editing, daily/weekly/monthly calendar, separate simulations, visit history, profile, publisher and future subscriptions. Preserve existing data, migrations and access controls. Do not invent active payment, prices or infrastructure.
Implement chronological greedy route planning with priority, interest, due dates, visit durations, shared-address grouping, workweek, holidays, leave, lunch and start/end locations. Zero account duration inherits the user's profile default (45 minutes initially). Use bounded IGN car routing with directional 30-day cache and explicit geographical fallback; do not claim live traffic or a global optimum. Implement protected confirmed appointments, explicit add/replace publishing, completion, cancellation, absence/rescheduling, snapshots and ICS exports with stable IDs, UTC/DST handling, status and UTF-8 folding.
Search all entered postal addresses via IGN/BAN after 650 ms idle time. Reject stale results. Use a consistent selectable list with double-click, Enter or confirmation button; fill all available address fields and GPS, preserving address extras. Keep archives/simulations read-only. Translate all labels, errors and exports, but never user data or technical CSV headers. Use the supplied XTR Conseil logo in every documentation page. Deliver online user, functional, technical and regeneration documentation with source manifest. Run temporary-database tests, every JS syntax check, bilingual and generated-documentation checks. Exclude private data and secrets; regenerate docs on every release and verify CI after every push. Open each new release in the external browser.
Interface mobile / Mobile UI : en-tête compact, navigation métier cachée avant connexion, Menu accessible avec aria-expanded après connexion, champs 16 px et 44 px de haut, formulaires adaptatifs. Compact header, workspace navigation hidden before sign-in, accessible Menu afterwards, 16 px fields and 44 px touch targets, responsive forms.
Provide Forgot password with local administrator issuance, hashed single-use 30-minute codes, uniform request response, rate limits and revocation of all sessions on reset. No automatic email until a mail service is configured. Prévoir Mot de passe oublié avec remise privée par l’administrateur local, code haché à usage unique de 30 minutes, réponse uniforme et révocation de sessions.
Staging / Préproduction : conserver le mode local par défaut. Reproduire deployment.py, le provisionnement fictif et les tests ; seuls un domaine HTTPS explicite et une base marquée demo-only autorisent le mode staging. Inscriptions interdites, cookies Secure, contrôles Host/Origin, bind loopback derrière Caddy. Ne jamais recopier la base locale sur un serveur public. Public deployment requires the separate documented acceptance checks.
Complément FR — Rendez-vous fixes
Implémenter l’édition de 0 à 24 rendez-vous fixes par journée dans le dialogue agenda : titre libre, début/fin et lieu du profil. Ne jamais les déplacer pour optimiser une tournée. Réserver les déplacements, rejeter les collisions et itinéraires impossibles, conserver les créneaux lors des annulations et RAZ, supprimer uniquement explicitement. Afficher hors tournée, archiver et exporter ICS confirmé ; ne pas compter comme visite commerciale. Appliquer aussi ces contraintes au merge et au recalcul. Voir TECHNIQUE.md et tests/test_fixed.py.
EN addition — Fixed appointments
Implement 0–24 fixed appointments per day in the calendar dialog: free-text title, start/end and profile location. Never move them for route optimization. Reserve travel time, reject overlaps and impossible routes, preserve slots across visit cancellation and resets, remove only explicitly. Display even without a route, archive and export as confirmed ICS events; do not count as account visits. Enforce constraints during merge and rebuild too. See TECHNIQUE.md and tests/test_fixed.py.
Version 0.18 — Rendez-vous à la même adresse
À chaque calcul de simulation ou recalcul après report, répondez à « Grouper les rendez-vous des comptes à la même adresse ? ». Accepter partage une seule durée standard de Mon compte entre les comptes éligibles dont la durée spécifique est zéro. Refuser conserve la durée complète de chaque visite. Trois comptes standards de 45 minutes prennent chacun 15 minutes ; deux standards prennent 23 et 22 minutes, plus la durée intégrale de tout compte personnalisé. L’arrondi à la minute conserve exactement le total. Une durée personnalisée reste individuelle même si elle est égale à la durée standard.
Les rendez-vous gardent leur compte, leur statut et leur historique individuels. Seuls les comptes sélectionnés, à visiter et disponibles sont inclus. Les heures d’ouverture, heures préférentielles, déjeuner et rendez-vous fixes restent respectés ; si le créneau commun est incompatible, les visites restent individuelles. Les groupes sont limités au nombre de minutes du créneau pour éviter une durée nulle. La réponse est conservée avec la simulation pour son enregistrement et son transfert à l’agenda, puis redemandée au prochain calcul.
Version 0.18 — Appointments at the same address
Every simulation calculation or recalculation after rescheduling asks “Group appointments for accounts at the same address?”. Accept to share one standard duration from My account between eligible accounts with a zero custom duration. Decline to retain each full visit duration. Three standard accounts share 45 minutes as 15 each; two share it as 23 and 22 minutes, plus the full duration of any custom appointment. Minute rounding preserves the exact total. Custom durations stay individual even when equal to the standard duration.
Each appointment retains its own account, status and history. Only selected, visitable and available accounts participate. Opening hours, preferred times, lunch and fixed appointments remain respected; incompatible shared slots fall back to individual visits. Group size is limited to the slot’s number of minutes to prevent zero durations. The choice is stored with the simulation for saving and agenda transfer and is asked again on the next calculation.
Version 0.18.1 — Statut par double-clic
Dans l’agenda actuel, double-cliquez sur une visite puis choisissez son statut dans la liste : Planifié, Confirmé par le compte, Réalisé, Absent / à reprogrammer ou Annulé. Cliquez sur Enregistrer le statut. Réalisé ouvre la saisie de durée réelle et compte rendu et n’est disponible qu’à partir du jour du rendez-vous. Une absence ouvre la reprogrammation et une annulation demande confirmation. Les erreurs restent visibles dans la fiche. Simulations et archives restent en consultation ; les visites terminées conservent leur historique.
Version 0.18.1 — Double-click appointment status
In the current calendar, double-click a visit and choose its status: Planned, Confirmed by account, Completed, Absent / reschedule or Cancelled. Click Save status. Completed opens actual duration and notes and is available from the appointment date. Absence opens rescheduling; cancellation requires confirmation. Errors remain visible in the dialog. Simulations and archives are read-only; finished visits retain their history.
Version 0.18.2 — Lisibilité des trajets
Les trajets occupent la largeur disponible dans la colonne du jour, au lieu d’une bande de 16 pixels. Leurs heures de début et de fin apparaissent en premier, puis leur durée et leur adresse selon la hauteur disponible. Un trajet très court garde une hauteur proportionnelle à sa durée ; le survol ou le focus développe son détail. Les horaires et les données ne changent pas.
Version 0.18.2 — Readable travel blocks
Travel blocks use the available day-column width instead of a 16-pixel strip. Start and end times come first, followed by duration and address when height permits. Very short trips keep a height proportional to their duration; hover or keyboard focus expands their details. Times and stored data are unchanged.
Version 0.18.3 — Sauvegarde des lieux
Dans Mon compte, « Sauvegarder mes lieux et paramètres » reste visible pendant le défilement ; un second bouton est disponible en bas. La saisie, l’ajout de lieu et le choix d’adresse IGN sont suivis comme modifications non sauvegardées. Quitter la rubrique, suivre un lien dans le même onglet ou se déconnecter demande confirmation : Non conserve la saisie, Oui abandonne. Échap équivaut à Non. Un nouvel appui sur Mon compte ne réinitialise pas le formulaire. Actualisation/fermeture : avertissement natif du navigateur, avec son propre texte. La protection disparaît après une sauvegarde réussie, reste après erreur et protège les changements effectués pendant un enregistrement. Elle concerne le formulaire des lieux et horaires ; les autres formulaires ont leur propre enregistrement.
Version 0.18.3 — Saving locations
In My account, “Save my locations and settings” stays visible while scrolling, with another button at the bottom. Edits, adding a location and selecting an IGN address mark the form as unsaved. Leaving the section, following a same-tab link or signing out asks for confirmation: No keeps the draft, Yes discards it. Escape means No. Clicking My account again does not reset the form. Reloading or closing the page uses the browser’s native warning and wording. Protection clears after successful saving, remains after errors and preserves edits made during a save. It covers the locations and schedule form; other forms have separate save actions.
Version 0.19 — Sauvegarde automatique
Les lieux et horaires de Mon compte sont sauvegardés en brouillon sur le serveur une seconde après la dernière modification. Une adresse incomplète est conservée sans être validée ; « Sauvegarder mes lieux et paramètres » applique explicitement le formulaire et conserve les contrôles habituels. Le brouillon revient à la prochaine ouverture de Mon compte. L’indicateur confirme la sauvegarde automatique ; en cas d’erreur réseau, gardez la page ouverte et réessayez en modifiant un champ. Une fermeture avant la fin de la sauvegarde déclenche l’avertissement du navigateur. Abandonner explicitement supprime le brouillon.
Chaque calcul réussi conserve automatiquement la dernière simulation par utilisateur. Elle est restaurée au chargement et consultable dans Agenda de simulation ; elle ne publie aucun rendez-vous. Vérifiez-la avant transfert, notamment après changement de paramètres. Les simulations nommées restent disponibles séparément. Les brouillons sont inclus dans les sauvegardes complètes du serveur, pas dans les exports JSON par catégorie. Un import utilisateur efface les brouillons pour éviter des références obsolètes. Deux onglets modifiant le même brouillon suivent la règle du dernier enregistrement ; utilisez un seul onglet de saisie.
Version 0.19 — Automatic saving
My account locations and schedules are saved as a server-side draft one second after the last edit. Incomplete addresses are preserved without being validated; “Save my locations and settings” explicitly applies the form with the usual checks. The draft returns when My account is next opened. The indicator confirms automatic saving; on network failure, keep the page open and retry by editing a field. Closing before saving completes triggers the browser warning. Explicitly discarding removes the draft.
Every successful calculation automatically retains the latest simulation per user. It is restored on loading and available in Simulation calendar; no appointments are published. Review before transfer, especially after changing settings. Named simulations remain separately available. Drafts are included in full server backups, not in category-based JSON exports. User imports clear drafts to avoid stale references. Concurrent tabs use last-save-wins behavior; use one editing tab.
Version 0.20 — Coordonnées des comptes
Les fiches et la liste du portefeuille affichent Téléphone professionnel, Mobile professionnel et E-mail professionnel, modifiables par compte. Le modèle CSV ajoute phone, mobile et professional_email sans renommer les colonnes existantes. Les sauvegardes JSON conservent ces champs.
Enrichissement administratif : scripts/enrich_contacts.py utilise l’Annuaire Santé RPPS (identifiant RPPS + structure) et Ameli (nom complet + voie normalisée + code postal + commune). Il complète uniquement les champs vides à partir de valeurs publiques non ambiguës, classe les numéros français 06/07 comme mobiles professionnels publiés, conserve les saisies et trace la source et la date de vérification par champ. Aucun e-mail déduit, aucune messagerie MSSanté ajoutée pour le contact commercial. Mode prévisualisation par défaut ; --apply crée une sauvegarde SQLite puis met à jour uniquement l’utilisateur sélectionné, sans modifier son agenda, ses notes ou ses propriétaires. Fichiers sources et bases privés, exclus de Git. Sources : https://www.data.gouv.fr/datasets/annuaire-sante-ameli et https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.20 — Account contact details
Account forms and portfolio rows show editable Business phone, Business mobile and Business email fields. The CSV template appends phone, mobile and professional_email without renaming existing columns. JSON backups preserve these fields.
Administrative enrichment: scripts/enrich_contacts.py uses Annuaire Santé RPPS (RPPS + practice identifier) and Ameli (full name + normalized street + postal code + town). Only empty fields are filled from unambiguous public values; published French 06/07 numbers are classified as business mobiles. Existing values remain unchanged and each addition records its source and verification date. No email guessing or MSSanté mailbox enrichment for business contacts. Preview is the default; --apply backs up SQLite and updates only the selected user without changing calendar, notes or owners. Source files and databases stay out of Git. Sources: https://www.data.gouv.fr/datasets/annuaire-sante-ameli and https://www.data.gouv.fr/datasets/annuaire-sante-extractions-des-donnees-en-libre-acces-des-professionnels-intervenant-dans-le-systeme-de-sante-rpps.
Version 0.21.0 — team licences and AI professional lists
The /licence page presents individual subscriptions and annual-only team packs: up to 5 users including the owner, proposed at EUR 699 including VAT; up to 10 at EUR 1,199 including VAT. Team prices require commercial approval before activation. For more than 10 users, contact XTR Conseil. Owners add or remove existing registered users. Workspaces stay separate and private. Seat limits are enforced on the server. Administrators edit prices and bilingual descriptions; zero hides an offer.
The /administration page is restricted to server-configured administrator emails. It covers users, blocking, free grants, password resets, estimated storage, workspace consultations, orders, invoices, prices and AI lists. A free grant does not issue a paid invoice. Manual payments require a receipt reference and explicit confirmation. PDF invoices are generated from order snapshots and retained. JSON invoice data prepare an accredited electronic invoicing platform connection; they do not perform regulatory transmission.
To prepare an AI list, enter a profession and area, choose a limit of 1 to 100, supply public source text as needed, build and edit the prompt, then save it. Under Providers, configure an exact model identifier and API key for OpenAI, Claude, DeepSeek or Gemini. Keys are encrypted on the server and never returned to the interface. Generation requires confirmation of a potentially billable call. OpenAI uses web search if supported by the model; the other three adapters analyse supplied text without web browsing in this version. Results may be empty and are never described as exhaustive or certified.
Results remain drafts for review. Open sources, check contact details, select rows and a target user workspace, then confirm import. Rows without a source cannot be imported. Matching name/address duplicates and repeated imports are avoided. AI-generated coordinates are discarded: validate addresses before route planning. No portfolio or calendar is automatically sent to an AI provider.
Delivery status: local development, adapters tested with simulated responses; no real AI call without keys. Stripe and no-reply@visit-manager.fr email require configuration and end-to-end verification. Consumer sales remain disabled. This preparatory version does not claim a deployed administration subdomain or commercial launch. Existing data are preserved. Grant appropriate access to beta testers before enabling licence enforcement.
Version 0.21.1 — official company search
Account creation/editing, My account billing details and licence checkout now provide dynamic French company search by name, SIREN or SIRET. Results show each establishment’s SIRET, address, status and head-office marker. Selecting a result fills the name, address, SIREN, SIRET, available VAT number, activity code and legal category. Review and save the form: selection alone does not save. Fields remain editable and manual entry remains available on service failure. The public Company Search API may omit non-public entities. No VAT number is calculated; an API-supplied number is not VIES validation. Only the search text is sent to the official service. Stale responses are discarded, requests are rate-limited and authentication is required. SIRET and VAT are preserved with account or billing records.
Licence presentation: smaller headings and prices, five offers aligned on wide screens, adaptive tablet and phone layout, and a compact centred sign-in form.
Version 0.22.0 — Accueil, souscription et contact
Les offres payantes et l’essai sont présentés sur l’accueil, avec prix provenant du catalogue. Le choix est conservé dans le parcours de création de compte ou de connexion, puis dans les coordonnées de facturation. La case « J’ai lu les conditions générales de vente et je les accepte » est obligatoire avant Stripe ; sa version et la date d’acceptation sont enregistrées. Aucun paiement n’est lancé lorsque Stripe ou les autorisations commerciales ne sont pas configurés. Les inscriptions privées et les ventes aux particuliers restent protégées par leurs verrous existants.
Le formulaire public Contact propose information, technique, licence/facturation, suggestion, confidentialité et autre thème libre. Chaque demande reçoit une référence VM-année-numéro ; les répétitions du même envoi ne créent pas de doublon. L’administration affiche les 200 dernières demandes et permet les statuts nouvelle/en cours/clôturée. Les messages destinés à xtr-conseil@orange.fr et les accusés de réception sont conservés dans la file d’envoi. L’expéditeur souhaité est no-reply.xtr-conseil@orange.fr, confirmé existant par l’éditeur. Sa configuration SMTP reste nécessaire ; sans elle, l’enregistrement reste possible mais aucun e-mail n’est envoyé.
Les CGV et la confidentialité précisent l’absence de prospection ou d’exploitation commerciale, les sauvegardes sur l’infrastructure OVHcloud et la recommandation d’exports personnels. Elles distinguent l’absence de consultation des portefeuilles dans l’administration et la possibilité d’accès technique au serveur : aucune garantie de chiffrement de bout en bout n’est annoncée. Communications limitées au service, au contrat et aux réponses aux sollicitations.
Version 0.22.0 — Home, purchase and contact
Paid plans and the trial appear on the home page using catalogue prices. The selected plan persists through registration or sign-in and billing details. The unchecked “I have read and accept the terms of sale” box is mandatory before Stripe; the terms version and acceptance timestamp are recorded. No payment starts without Stripe and approved commercial settings. Existing private-registration and consumer-sale gates remain in place.
The public contact form offers information, technical, licence/billing, improvement, privacy and a custom topic. Each request receives a VM-year-number reference; retrying the same submission does not duplicate it. Administrators can view the latest 200 requests and mark them new, in progress or closed. Notifications to xtr-conseil@orange.fr and acknowledgements are queued. The requested sender is no-reply.xtr-conseil@orange.fr, confirmed by the publisher to exist. SMTP still needs configuration; requests are recorded without sending email when it is unavailable.
Terms and privacy prohibit marketing or commercial exploitation, describe backups on OVHcloud infrastructure and recommend personal exports. They distinguish portfolio access through the management UI from possible technical server access; no end-to-end encryption guarantee is made. Communications are limited to the service, contract and replies to user requests.
Version 0.22.1 — Historique des demandes
L’administration conserve les messages reçus avec un historique daté des actions et de leur auteur. Deux suivis distincts : dossier Nouveau / En cours / Clôturé ; réponse À répondre / Répondu / Réponse non nécessaire. Une note interne est obligatoire lors du passage à Répondu ou Réponse non nécessaire. Elle décrit la réponse effectuée ailleurs ou le motif ; l’enregistrement ne transmet aucun e-mail. L’accusé automatique ne vaut pas réponse. Les messages originaux et les anciennes notes ne sont pas écrasés.
Recherche par référence, nom, e-mail ou objet ; filtres par statut et réponse. Les demandes sont chargées par pages de 50, avec accès aux plus anciennes. Les demandes existantes gardent leur statut et reçoivent un point de départ d’historique, sans inventer de réponse passée. Un contrôle de révision refuse les modifications concurrentes devenues obsolètes.
Version 0.22.1 — Contact history
Administration retains received messages and a dated history of actions and their author. Track case status (New / In progress / Closed) separately from response status (Awaiting response / Answered / No response needed). An internal note is required when marking a request answered or unnecessary. It records a response sent separately or a reason; saving sends no email. Automatic acknowledgement is not a response. Original messages and earlier notes remain unchanged.
Search by reference, name, email or subject and filter by case and response status. Requests load in pages of 50, including older records. Existing cases retain their status and receive a history baseline without inventing earlier responses. Revision checks reject stale concurrent updates.
Mentions légales / Legal notice (18/09/2026) : préserver la page bilingue mentions-legales.html, legal.css et les liens depuis les pages publiques ; éditeur XTR Conseil et hébergeur OVH pour les instances hébergées. Keep the bilingual public legal notice and links, separately from service-specific terms and privacy. See docs/MENTIONS_LEGALES.md.
Version 0.22.2 — Sécurité de l’administration
L’onglet Sécurité permet de changer son propre mot de passe : mot de passe actuel, nouveau mot de passe (12 à 256 caractères), confirmation. Seul un administrateur connecté peut effectuer cette action. Cinq tentatives de vérification maximum sur quinze minutes ; les erreurs sont traduites. Les mots de passe sont hachés et ne figurent pas dans l’historique. Toutes les sessions et les codes de réinitialisation de ce compte sont invalidés après succès ; une nouvelle connexion est obligatoire. Les autres utilisateurs ne sont pas modifiés.
Prise en charge préparée pour admin.visit-manager.fr : définir VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, ajouter son DNS et le site Caddy fourni dans deploy/staging/admin.Caddyfile.example, puis vérifier HTTPS. Préserver l’en-tête Host du sous-domaine. Les origines sont vérifiées séparément pour chaque hôte ; les cookies restent propres à chaque hôte. L’API du sous-domaine refuse les accès non administratifs. Sous-domaine activé le 18 septembre 2026 : DNS OVH, certificat HTTPS et accès administrateur vérifiés. L’administration existante conserve son chemin /administration.
Version 0.22.2 — Administration security
The Security tab changes the signed-in administrator’s own password: current password, new password (12–256 characters) and confirmation. Verification is limited to five attempts per fifteen minutes. Passwords are hashed and excluded from audit records. Success invalidates every session and reset token for this account; signing in again is required. Other users remain unchanged.
Support is prepared for admin.visit-manager.fr: configure VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, add DNS and the Caddy site in deploy/staging/admin.Caddyfile.example, then verify HTTPS. Preserve the subdomain Host header. Origins are validated separately per host and cookies remain host-only. Non-administrative APIs are rejected on the subdomain. Activated on 18 September 2026: OVH DNS, HTTPS certificate and administrator access verified. The existing /administration path remains available.
Portefeuille compact / Compact portfolio
Le portefeuille adopte une barre d’actions alignée, des filtres répartis sur plusieurs colonnes et une liste plus compacte. Sur téléphone, les commandes se réorganisent ; le tableau conserve son défilement horizontal. Les boutons Masquer/Afficher les filtres restent disponibles.
The portfolio uses an aligned action bar, multi-column filters and a more compact list. On phones, controls reflow and the table retains horizontal scrolling. Hide/Show filters remains available.
CSS limited to #clients: account-tools, account-filter-card, account-table. Preserve IDs, event handlers, translations, native file upload and existing account data. Breakpoints: 1100, 700 and 420 px. No server or database changes.
Modèles CSV localisés / Localized CSV templates
Le bouton Modèle CSV télécharge les en-têtes dans la langue active, avec une deuxième ligne fictive à remplacer ou supprimer. Formats du modèle ouvre l’aide de chaque colonne (dates AAAA-MM-JJ, heures HH:MM, durées en minutes, codes et téléphones à importer comme texte). L’import accepte les en-têtes français, anglais et techniques historiques, indépendamment de la langue affichée. Les virgules décimales sont acceptées pour les coordonnées. Les doublons d’en-têtes et les lignes mal formées sont rejetés avant ajout.
CSV template downloads headers in the active language, with a fictional second row to replace or delete. Template formats opens per-column guidance (YYYY-MM-DD dates, HH:MM times, durations in minutes, codes and phone numbers imported as text). Imports accept French, English and historical technical headers regardless of interface language. Decimal commas are accepted for coordinates. Duplicate headers and malformed rows are rejected before insertion.
Schema: web/csv-columns.json (keys, FR/EN headers, examples and format guidance). csv_accounts.read_accounts normalizes header accents/case/spacing and retains historical metadata. User-entered text remains unchanged.
Création d’utilisateur et accès temporaire (12 heures)
L’administrateur crée le compte, choisit sa langue et reçoit un mot de passe temporaire affiché une seule fois. Il peut le transmettre directement ou utiliser « Envoyer un mot de passe temporaire » : ce bouton génère un nouveau secret, invalide le précédent et envoie les instructions par e-mail. L’utilisateur doit choisir un mot de passe différent dès la première connexion, avant tout accès aux données. Après 12 heures, une nouvelle invitation est nécessaire. Aucun mot de passe en clair n’est conservé dans les journaux ni les files d’envoi. Vérifier le résultat SMTP ; un échec ou résultat incertain est signalé. Aucun envoi automatique à la création.
User creation and temporary access (12 hours)
The administrator creates an account, selects its language and receives a temporary password displayed once. They can share it directly or use “Send temporary password”: this generates a fresh secret, invalidates the previous one and emails instructions. The user must choose a different password on first sign-in before accessing any data. After 12 hours, issue a new invitation. Passwords are not stored in clear text in logs or mail queues. Check the SMTP outcome; failed or uncertain delivery is reported. Creation does not send email automatically.
Languages: Visit Manager FR/EN; AO Manager FR; Expertise Manager FR; Dental Prep fr, en-GB, de, es, it, pt-PT, sv, da, nl-NL, nl-BE, ar. Excludes mapsychopraticienne.fr/.com. Visit Manager: temporary_access table, admin-invitation endpoint, SHA-256 hash of a random high-entropy secret, one-time 15-minute reset challenge bounded by the original 12-hour expiry; no business session before reset. Regeneration deletes earlier reset tokens and sessions. Existing accounts and role checks remain unchanged.
Staging
Beta update: on the owner’s explicit request, a real portfolio may be transferred to their individual account. Require VISIT_STAGING_ACK=authorized-real-data and the matching staging_meta purpose=authorized-real-data marker; demo-only remains the default for fictional databases. Retain HTTPS, per-user isolation and closed registration. Back up before import and preserve account fields. The banner now states “Private beta”. Never commit secrets or data exports.
Staging is available at https://visit-manager.fr as of 17 September 2026. OVH VPS deployment uses Caddy HTTPS, a loopback-only Python service, a separate demonstration database and disabled public registration. Individual credentials are shared privately; real data requires explicit authorization. Daily SQLite backups retain 14 copies on the VPS, in addition to OVH backup. Automatic email password recovery remains unconfigured. This package does not purchase or deploy a server. Python listens only on 127.0.0.1; Caddy on the same Linux host provides HTTPS. Intended for limited acceptance testing, not general SaaS production. External TLS/network validation remains required.
Deploy code only to /opt/visit-manager-staging under a dedicated visit-staging system user. Never copy the entire local database; transfer only explicitly authorized account records. Reserve /var/lib/visit-manager-staging with owner-only permissions. Use supported Python 3.10+ and Caddy installations. Review existing AO Manager ports/sites before any host change.
Provision each tester with python3 scripts/create_demo_tester.py --db /var/lib/visit-manager-staging/visit.sqlite --email tester@example.com. The script refuses local/unmarked databases and existing accounts, seeds twelve fictional accounts and prints a unique random password once. Share credentials privately, never in Git or shared logs. Public registration is disabled; no automatic email is sent.
Adapt deploy/staging/environment.example, install its values in /etc/visit-manager-staging.conf (600), review/install the systemd unit. Staging requires an HTTPS origin, explicit isolated database and demo-only marker. Append the supplied Caddy site without replacing existing configuration; set STAGING_HOST to the approved hostname, matching VISIT_PUBLIC_ORIGIN without its scheme. Validate Caddy configuration before reloading. Only 80/443 may be public; 8791 must remain loopback-only. Configure DNS only for the approved subdomain, including any IPv6 records.
Before sharing: validate the real HTTPS certificate, disabled registration, two independent tester accounts, isolation, Secure/HttpOnly/SameSite cookies, foreign-origin rejection, forms/planning/ICS, closed public port 8791 and absence of real data. Local tests do not establish external deployment readiness. Keep consistent SQLite backups and release records. To suspend, remove only this Caddy site and stop visit-manager-staging.
Host, domain and first tester are configured. External checks passed: valid HTTPS certificate, login and demo portfolio retrieval. Test duration and data retention remain to be specified. MX 5 email is separate from this hosting.
Production preparation
The production, business_api, professional_lists, invoice_pdf and mail_delivery modules provide licensing, seat caps, orders, audited administration, encrypted AI configuration, reviewed contact drafts, PDF invoices and queued email. Install requirements.txt. Startup adds tables without deleting existing data. User backups and API responses exclude AI credentials; server SQLite backups include encrypted credentials.
Keep server settings outside Git: VISIT_ADMIN_EMAILS (explicit allowlist; provision the account before public registration), VISIT_PRODUCTION, VISIT_PRICES_APPROVED, VISIT_TEAM_PRICES_APPROVED, VISIT_VAT_CONFIRMED and Stripe secret/webhook secret. Existing HTTPS staging mode remains in use during preparation. Do not enable enforcement before migrating beta entitlements and verifying billing. Provision a strong individual administrator credential, never a shared test password.
VISIT_AI_MASTER_KEY is a server-generated Fernet key, stored securely and backed up separately from SQLite. Losing it requires re-entering provider credentials. Rotation requires re-encryption or re-entry. Provider destinations are fixed HTTPS endpoints; redirects are rejected; responses are capped at 2 MB and output at 12,000 tokens. Raw provider errors are suppressed. Models are explicitly configured. Failed calls are never automatically retried. No real provider calls were made during testing. Set spending caps at the provider. AI coordinates are discarded. Contact import requires explicit review, public sources and deduplication. Drafts persist and are administrator-only.
The signed Stripe webhook validates timestamp, session, amount, currency and test/live mode. Payment activation and invoice creation are transactional and idempotent. Browser redirects never activate licences. Seat caps include the owner; oversized teams prevent downgrades. Workspaces remain separate. Invoice JSON is preparatory data, not Factur-X or accredited transmission. Credit-note processing and accredited platform integration remain to be completed before full commercial operations.
SMTP uses VISIT_SMTP_HOST, VISIT_SMTP_PORT, VISIT_SMTP_USER and VISIT_SMTP_PASSWORD, with TLS. Verify that OVH permits no-reply@visit-manager.fr before enabling delivery. Run scripts/deliver_business_mail.py daily in the service environment. It queues 14/7/1-day reminders and sends pending invoices. Without SMTP configuration it sends nothing. Uncertain deliveries require review rather than automatic retries. Reminders use the last FR/EN preference saved on workspace or licence-page access (French by default). Password recovery still uses privately shared administrator-generated reset codes, valid for one hour; SMTP recovery is not connected yet. Never email passwords.
Consumer sales stay disabled. Mediator and consumer-sale environment flags are technical gates, not compliance approval. Complete mediator details, online withdrawal, durable terms confirmation and legal review before activation. Terms are bilingual preparatory documents. Sales are limited to France.
Consultations count authenticated application-state reads, not unique website visitors. Storage is a logical estimate of user JSON and PDFs, excluding indexes and backups. The administration subdomain still needs DNS/TLS and an origin policy. Use /administration during development. This local release does not change DNS or open production.
Sources
https://developers.openai.com/api/docs/guides/text
https://developers.openai.com/api/docs/guides/tools-web-search
https://platform.claude.com/docs/en/api/messages/create
https://api-docs.deepseek.com/
https://ai.google.dev/api/generate-content
https://www.impots.gouv.fr/facturation-electronique-et-plateformes-agreees
https://entreprendre.service-public.gouv.fr/vosdroits/F33527
0.22.0 — Contact delivery / Envoi contact
FR : contact.py conserve les demandes dans contact_cases. Routes publiques GET /api/contact-token et POST /api/contact, origine contrôlée, jeton signé valable entre 2 secondes et 2 heures, leurre anti-robot, bornes de saisie, 3 demandes par adresse et 30 globales par heure. Le même jeton rejoué avec les mêmes données renvoie la même référence sans nouvel envoi. L’accusé ne reprend pas le message du demandeur. Admin : /api/admin-contact avec session, CSRF et rôle explicite. Aucun envoi réel pendant les tests.
Configurer VISIT_SMTP_HOST/PORT/USER/PASSWORD avec un compte autorisé pour l’expéditeur, VISIT_CONTACT_FROM=no-reply.xtr-conseil@orange.fr et VISIT_CONTACT_FROM_VERIFIED=1 après vérification de l’autorisation d’envoi. L’existence de la boîte seule ne suffit pas à garantir l’autorisation d’envoi SMTP. Tant que ce verrou n’est pas activé, les deux e-mails de contact restent en attente ; les autres e-mails ne sont pas bloqués. Exécuter scripts/deliver_business_mail.py régulièrement (par exemple chaque minute) pour traiter la file ; les rappels restent dédoublonnés. Aucun réessai automatique des envois incertains.
EN: contact.py stores contact_cases. Public GET /api/contact-token and POST /api/contact enforce Origin, a signed token aged 2 seconds to 2 hours, a honeypot, bounded inputs, 3 requests per email and 30 globally per hour. Identical token retries reuse the reference without new emails. Acknowledgements never quote the submitted message. /api/admin-contact requires session, CSRF and explicit admin role. No real email was sent in tests.
Configure SMTP credentials authorised for VISIT_CONTACT_FROM=no-reply.xtr-conseil@orange.fr and set VISIT_CONTACT_FROM_VERIFIED=1 only after verifying sender permission. Mailbox existence alone is insufficient. Both contact emails remain pending until configured; other mail is not blocked. Run scripts/deliver_business_mail.py regularly (e.g. each minute); reminders remain deduplicated. Uncertain deliveries are not automatically retried.
Version 0.22.1 — Historique des demandes
L’administration conserve les messages reçus avec un historique daté des actions et de leur auteur. Deux suivis distincts : dossier Nouveau / En cours / Clôturé ; réponse À répondre / Répondu / Réponse non nécessaire. Une note interne est obligatoire lors du passage à Répondu ou Réponse non nécessaire. Elle décrit la réponse effectuée ailleurs ou le motif ; l’enregistrement ne transmet aucun e-mail. L’accusé automatique ne vaut pas réponse. Les messages originaux et les anciennes notes ne sont pas écrasés.
Recherche par référence, nom, e-mail ou objet ; filtres par statut et réponse. Les demandes sont chargées par pages de 50, avec accès aux plus anciennes. Les demandes existantes gardent leur statut et reçoivent un point de départ d’historique, sans inventer de réponse passée. Un contrôle de révision refuse les modifications concurrentes devenues obsolètes.
Version 0.22.1 — Contact history
Administration retains received messages and a dated history of actions and their author. Track case status (New / In progress / Closed) separately from response status (Awaiting response / Answered / No response needed). An internal note is required when marking a request answered or unnecessary. It records a response sent separately or a reason; saving sends no email. Automatic acknowledgement is not a response. Original messages and earlier notes remain unchanged.
Search by reference, name, email or subject and filter by case and response status. Requests load in pages of 50, including older records. Existing cases retain their status and receive a history baseline without inventing earlier responses. Revision checks reject stale concurrent updates.
Version 0.22.2 — Sécurité de l’administration
L’onglet Sécurité permet de changer son propre mot de passe : mot de passe actuel, nouveau mot de passe (12 à 256 caractères), confirmation. Seul un administrateur connecté peut effectuer cette action. Cinq tentatives de vérification maximum sur quinze minutes ; les erreurs sont traduites. Les mots de passe sont hachés et ne figurent pas dans l’historique. Toutes les sessions et les codes de réinitialisation de ce compte sont invalidés après succès ; une nouvelle connexion est obligatoire. Les autres utilisateurs ne sont pas modifiés.
Prise en charge préparée pour admin.visit-manager.fr : définir VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, ajouter son DNS et le site Caddy fourni dans deploy/staging/admin.Caddyfile.example, puis vérifier HTTPS. Préserver l’en-tête Host du sous-domaine. Les origines sont vérifiées séparément pour chaque hôte ; les cookies restent propres à chaque hôte. L’API du sous-domaine refuse les accès non administratifs. Sous-domaine activé le 18 septembre 2026 : DNS OVH, certificat HTTPS et accès administrateur vérifiés. L’administration existante conserve son chemin /administration.
Version 0.22.2 — Administration security
The Security tab changes the signed-in administrator’s own password: current password, new password (12–256 characters) and confirmation. Verification is limited to five attempts per fifteen minutes. Passwords are hashed and excluded from audit records. Success invalidates every session and reset token for this account; signing in again is required. Other users remain unchanged.
Support is prepared for admin.visit-manager.fr: configure VISIT_ADMIN_ORIGIN=https://admin.visit-manager.fr, add DNS and the Caddy site in deploy/staging/admin.Caddyfile.example, then verify HTTPS. Preserve the subdomain Host header. Origins are validated separately per host and cookies remain host-only. Non-administrative APIs are rejected on the subdomain. Activated on 18 September 2026: OVH DNS, HTTPS certificate and administrator access verified. The existing /administration path remains available.
Déploiement / Deployment · 18 septembre 2026
Version 0.22.2 publiée sur visit-manager.fr et admin.visit-manager.fr. Sauvegarde SQLite préalable et vérification des données existantes (1 717 comptes, deux utilisateurs). Les paramètres commerciaux, Stripe et SMTP restent inchangés et ne sont pas activés par cette publication. Le site et les API administrateur ont été vérifiés en HTTPS avec séparation des origines.
Version 0.22.2 deployed to visit-manager.fr and admin.visit-manager.fr. A prior SQLite backup and existing-data checks preserved 1,717 accounts and two users. Commercial, Stripe and SMTP settings remain unchanged and are not activated by deployment. HTTPS, administrator access and per-host origin isolation were verified.
API · 0.23.0
/api/account
/api/addresses
/api/admin-
/api/agenda-archive
/api/agenda-archive-read
/api/agenda-day
/api/agenda-export
/api/agenda-leave
/api/agenda-leave-delete
/api/agenda-publish
/api/agenda-recalculate
/api/agenda-simulation-export
/api/appointment-status
/api/appointments-cancel
/api/backup-export
/api/backup-import
/api/backup-preview
/api/billing-
/api/billing-offers
/api/calendar-display
/api/calendar-holidays
/api/calendar-import
/api/calendar-preview
/api/client
/api/company-search
/api/config
/api/contact
/api/contact-token
/api/delete-scenario
/api/identity
/api/import
/api/login
/api/logout
/api/password-forgot
/api/password-reset
/api/payment-webhook
/api/plan
/api/profile
/api/profile-draft
/api/register
/api/reschedule
/api/save
/api/scenario
/api/state
/api/subscription-checkout
/api/subscription-invoice-preview
/api/subscription-profile
/api/visit
SHA-256
{
"AGENTS.md": "ecab08fab5db51a714cc8a4a463e1fc47b7b79a9b593915244c0bde1ceaaf878",
"README.md": "2e8f023b486aa6149a02587cb181ebaf676d77bdf40b69efb3349f6d8e94701d",
"README_EN.md": "349edf05f21152aa5d30a083a7ec1c6f52894a2061b4d9ccc119ad5558996f22",
"agenda.py": "cc7ee53bd88aaaf2edba7fa191ee484881e6b9dc4f9a4ff1a1eec9384821757c",
"agenda_history.py": "a956d5de7f6871d81adabf3a6ef5487e0658d2a7bb5c17119249829ba68b6669",
"appointment_journal.py": "ee215aeffa6a851f35abec89fac13cb8107ac59119c5e0d09cd624e9d30dd095",
"appointments.py": "d4da86f82c0519b1e4722232cac2b91e90b5301a8ed13e8faa5728ef847cc868",
"business_api.py": "eeffa7a5079937975b3631a5c650cde842b87d5480f8bea9d9d06f26e5fd7f6e",
"calendar_import.py": "6b8b478e0f6623e9bf0bda3e324f01b5471f1f7a62efc6bd7e8ad9d6c612137d",
"commerce.py": "e7b78663e689c4c535649860428db759a0487eefe537f8ba46f6275523af95e2",
"companies.py": "2b3918565e4d083e23d86848889070fcd9f1abab14cacfd4f1242d0248aec1a2",
"contact.py": "61e4bb53386d115cb01f755da6c0874177de6238f7349e6a14f175453796fd1c",
"csv_accounts.py": "54a1e30b3b407271d7e0a5894a7d75537a0b354ece7b356adc6c5323693c7279",
"deploy/staging/Caddyfile": "9c0bd5855965f56c9343c3eb79f08de14eedd58c6cc8b1aefedddc70834b51af",
"deploy/staging/admin.Caddyfile.example": "4d5144d5a4a94c2e94e887aaa5bf38d1d99dcadb6455329a99f6443453406c9b",
"deploy/staging/environment.example": "0785ae06eb012cc2f5b57192716c7f0d8d319ee8a4ba2acbd983c1b30d6b4ada",
"deploy/staging/visit-manager-staging.service": "2f13639dbd2720479419378af201ac92bca558d0347b6268bb06b36c3e2c3f2c",
"deployment.py": "91c780f20fb162b85eda9bccc4a364935cd84e170e591c6c7d1d1781ca06f3cb",
"docs/CGV_PREPARATION.md": "4f070d692caedcf80719447b49670c24f5dec21cc72ec5144e715c5e4f93b821",
"docs/MENTIONS_LEGALES.md": "cb7a5b63f607dbfc8ea37de9f434232776c71f9a0da71aece7a1fa91f18af71a",
"docs/PREPRODUCTION.md": "354bfb22cac2647beb11ccfbbf4d815898de65d812adf5aa6a47e752386decb7",
"docs/PRODUCTION.md": "a3fa9235ba59f26b895fbbe3d3857c2c0e7ea2860fb7e40ae48f610be8948c67",
"docs/REGENERATION.md": "2b169960d3c49d5045ec77f9df14141469845936f39a2195ac8e31d4ef00dee9",
"docs/TECHNIQUE.md": "9e97f96ad00d7a4ef1cf14c5e1a4e75c924bfa98fb4c00db3decfe4312c5928b",
"docs/UTILISATEUR_EN.md": "3450de3cdf2cccc27dddf6be45d0c28fe1956434f22345ddc4a4e080b852dd53",
"docs/UTILISATEUR_FR.md": "a88d3ff25844f28fbcdbebb3512b2d4df68335e80a37bf7e73494421f720cd15",
"drafts.py": "b17a442f9101f9fda25110e774acdf834ecebf2d44fdc4190e0bd90f595aabb3",
"geocoding.py": "336ff982739c63be2ea02548c24f3fd966208df7746c076c82443d68287e4651",
"invoice_pdf.py": "dd393d49a5c9ceb409eeaf8b0cfdf1dc20da11594bbbdb98712854e5bc4db908",
"mail_delivery.py": "1abe23f29f6663e5d45246d4e73baba189017eb5efad7cf475df749b62498f6b",
"password_reset.py": "3979092887cd6720d32f133520a51a678ec51e02576b7f29dd2c95ce785a1784",
"planner.py": "901798400d520be768f176c60ca40789e72a80db91e6f5303b3e44e7f4b8812f",
"production.py": "c1ff35dc40c305c98efc6e1f2156143619463b7581b22ebf3b70a521d2e10519",
"professional_lists.py": "1a36d25f90a2ffafb295a9c7230eb8de1b0aad94f32a600c944d0ed508e5cc69",
"requirements.txt": "3737c6f7ec7effbc9b6e5f8efa6dcd6540cd51cedcfa4d13c192d4e91664ee70",
"resources/ariege-garonne.geojson": "3ebbacc78366c8d2e0f02982dc98d1127ff341fc4ec8661bca0a25b9feae23e9",
"routing.py": "2121945a22d08b45b4df1f5227a40555ec9fe2349fb7ae357edd51f2e4a30bf3",
"scripts/build_docs.py": "9074075c9fe85f3b9d6d7e8eaa898f3e06960c37935bba9fb3cf749a1dbc3ac2",
"scripts/create_demo_tester.py": "468642479b05f0df1a604ea26c3616d23f9b6f2093d43478ef7230a3f531eceb",
"scripts/deliver_business_mail.py": "2026f9c5a7c88cdee781d43fbc7d4e02864f2857b1585a0dd67d7f80355972c5",
"scripts/enrich_contacts.py": "b1c30f16fc3ce9e978939db62073d9f3af8ecfa479378f625253dd42b7773c2f",
"scripts/import_rpps_department.py": "560adc889030df327939ddc370add1a28df4c2338036505905277b4a6270027c",
"server.py": "2666eae872d5641bea08dcb403267883d7d496f30b54e266ce015fd2ff272611",
"temporary_access.py": "7bbf51bf695461c31c47f66ea8c68c3587eb70242527efc8a468364e7efaf05e",
"tests/appointment_status.js": "31698b8b44e96225341a19f9aeab81aa49c31b56eadc79e7d9d3dd1946a016d3",
"tests/portfolio_filters.js": "ba2f2e1131cd90c0971acf455a60503a9df797b11cf942dd6a7b10387c7ff07c",
"tests/profile_guard.js": "333cb2d35d5660d55ec4f99ee61d868df967fb4caa8b1b60b74c1f387f0f2592",
"tests/test_account_owner.py": "16129eda488a053330d5432cc50d6509833a09d2ed06ef2b6faf3a1458ac7645",
"tests/test_accounts.py": "946afb1a09b831c2d56281bd49e8904c85429405d8dc6449d4fb9abb92889c06",
"tests/test_agenda.py": "45ac4276d9601555f9ba37f485fc50c60a8513d7819f2b6fab9050b556e26f30",
"tests/test_app.py": "4bd09fcfd1a3b0341835aabdb8f9590b834701ed9dce401279cb819270a57442",
"tests/test_backup.py": "6f46421509ac8b21ae3cd49617b407a772188e7ca50f3c851733709d019bac15",
"tests/test_business.py": "56aa3b831dbb132a33ab166ebd8e9307ed4dadff04cd79055de62de704fff91f",
"tests/test_calendar_import.py": "72d2e0413b3ba7d340f432489b76f33d22744a8a4f0b9c8e4ed8ba3916351667",
"tests/test_companies.py": "67a5188d1fd5658c7b7e99db01d295f2f9e4a928f7b14def1dbc2b4e4a7ff2df",
"tests/test_contact_purchase.py": "fef7f08277fffebff6b358fd7ed728bd6e1e62938e90af208b94d6b7edafcff6",
"tests/test_contacts.py": "c25b51e5740679777546dbd7e1b8eb4122a5190dda473cd39eed55b059e60a3f",
"tests/test_csv_localized.py": "d67c7ff1b6abdfa3f953ee9a4eb2983f0822899b869baab34b6af4e99716ec2b",
"tests/test_department.py": "16f2aac8c7606c4bde592b8a0a70f72c95c00f1502315a667b5f89224b6deece",
"tests/test_drafts.py": "9409f7e99c7e1bb0ceb72a125c0661663a8b9fa86e71cd3740c13b13d25f8aea",
"tests/test_duration_defaults.py": "fe39824eded3023ae4815cf41fe6ffa7891baebdbd4a7a4340beb58740af6129",
"tests/test_fixed.py": "864b3b58db7f5e215dd665240d5e1bfbd43a2131dd1c06ce9f5d8451abd7f079",
"tests/test_grouping.py": "84ff0abae371402721939999228af3f5df24059d9dd31ac80ceced3b4d5137cb",
"tests/test_identity.py": "54a19bd4de8fcb08cb88e9cf344f8a81d1a7a32dd91be2871245359dde34f863",
"tests/test_journal.py": "98bff9001a66df64ab746bce843446a319f208c2a11d7425f9094eea2c446be0",
"tests/test_lifecycle.py": "02cc86246851a586d64b5e3caefb3eb9ee71c7446bd3537dac144ed0b1c9d8c1",
"tests/test_password_reset.py": "b1d0837834b0092d7c5cedabc1649681c6cc2efaeba10ae53fbdeb15ad0364eb",
"tests/test_preferred_time.py": "f18d1f8a21b483fab425496eabb27a2c3e600dc2b8cdd56978fdf80f658df990",
"tests/test_routing.py": "145997f9b85331e7b6bdf5193836281cf50c4174809805dade95761e60245570",
"tests/test_staging.py": "e4d0add5af1a55dc0ce1d34adead00527d0e184d619cd35774e1bdeed1cb5504",
"tests/test_temporary_access.py": "6d51a1c2603b22bf25cfc6272933de2b5b4978ef2c078a59b683ba5cb88edd19",
"tests/test_ui_status.py": "d4ef3c16b6f78748625127358f42c261c95050330698d7ce33827312e38f0fd6",
"tests/test_v03.py": "c96b3b1f31da79b494a5fed7d7d2df41e525a1e567680080e00efbc186e0e034",
"translations.py": "e9566b41fd32c4fedf1f47841ec9fefc1d6788cae7b997271c8ea78d81cfc527",
"user_backup.py": "7223b14135887fbbfa0ecfedc839eab338bea30fbb157d016bbab8f583551b35",
"web/agenda.js": "6e1566c6c2286806a0d9fc5ca9273da96a3e8f8afe3d884462948d9ae3e4ddd3",
"web/app.js": "46f151b7af3189de3db4a3b0a9b220035ecb8169930cf03d63c759fbea58544c",
"web/backup.js": "8d08776a0da4d4368a7bd34b5bfb18d746be757d6bfc81862217b02344bee9b9",
"web/business.css": "b506b8417351b64feb548fb14247bc1e908231467473b7c8253f1f549089771c",
"web/business.html": "62ea51465194aa4063834dcbed3728eb65b803d51758f031fa1dfce519cd4bb6",
"web/business.js": "3ae7dfc51b056c0731da1e572625698a622a5d0f83ef31bb9c3af324a82f68a6",
"web/calendar.css": "cd56e01a4ef8d1130957ae4fdcf58bcae7962b6f2be1e8c4b999413b9fcfa8b0",
"web/cgv.css": "aa465b6b8eae0e20c58ab41aeb3addc37f5ed5c91c58e35225c010d1ba1e40c4",
"web/cgv.html": "d86962bda5730d98ce8cbd8bc5478eb6a896cd1c18ca4a33f7015736f478cdb8",
"web/company-search.css": "5ef8179ac613602b0290133fae9f784502a0dea9dd47b94981435864561724e8",
"web/company-search.js": "d4e8e03292dcd0dae7ed24fcb487819988ecf4190632b6c96358ec67b0cdbd28",
"web/contact.html": "4044e7f921b33b912c0724217677fe75e8437a0bd80072c85e09f8b398608fd3",
"web/contact.js": "315c5a14e3e8f7e805a07ee0152bfb8ef96f1beb9fd314490e2192bf614b4f45",
"web/csv-columns.json": "24d13d00c14348009b7a8c148223baa296f093b7db415923151de2bfba873a0a",
"web/home.html": "6aefaf4bcb2a44e1bce41077289a229085d031c7652d16a4a23a70f8772ba7f6",
"web/home.js": "7581d6dee01dfb6c9f07a587706f3157e79290b48347567746a3bdfb9524a417",
"web/i18n.js": "f9bf85dcbc4c1978c064c31af0be70634a045716b98a128b11e223dc56895727",
"web/index.html": "c67ee016e81e748c713527fdaf10bf848df904c6c60fe5798d6f164a0fcfb5ad",
"web/legal.css": "0e5996ad463b1c9d5d3fc8ca3d757babcac43e18dc83690f5ab32ba074bb1047",
"web/lifecycle.js": "0d530851ec3ffe18edccc567c38e0ca64a7cd4c914e31192ed05fd1ef5346502",
"web/mentions-legales.html": "a9b478f8794fcd96ff0e14202d9ccddecc564dd6071251d331fbe2bac3ae6fca",
"web/messages.json": "452f4bea89f23b4f3dcf086fcae6256583c3b9a74ae4959f98be00c52e3f8484",
"web/privacy.html": "f991cdc491505363bfc5cea81a9ec6940d4a77b549f489f475a79e865ff85bf0",
"web/public.css": "4826751197685238b1cd7ad5d518d7f09b0c52c5054c69fb2b4fa567bf95a4c2",
"web/style.css": "a56a673a6788b74dee287004b971480b3c6ede62485935e31ee78456c61d1206",
"web/xtr-conseil.svg": "83915e751749e5bb754dcab42b595a6c3c3ba4e7a2ab7fb721483995c5183ae3",
"working_calendar.py": "3bf3b01180657f58b765771d71a1bdf0f47d36678928b007ae8d0fe9318e5c5a"
}