Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion app/blueprints/rdv_blueprint.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class RdvBlueprint < Blueprinter::Base
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il faudra aussi modifier RdvPlanBlueprint qui retourne rdv.starts_at. Peut-être que ça incite à mettre ces logiques de gestion de timezone dans un module séparé qui explique le problème.

identifier :id

fields :uuid, :status, :starts_at, :ends_at, :duration_in_min, :address, :context, :cancelled_at,
fields :uuid, :status, :duration_in_min, :address, :context, :cancelled_at,
:max_participants_count, :users_count, :name, :collectif, :created_by_type, :created_by_id, :created_at,
:visio_url

Expand All @@ -21,6 +21,22 @@ class RdvBlueprint < Blueprinter::Base
Rails.application.routes.url_helpers.agents_rdv_url(rdv, host: rdv.domain.host_name)
end

field :starts_at do |rdv, _options|
if rdv.motif.organisation.time_zone
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question de perf : est-ce que le motif et l'orga est préloadé ? D'ailleurs on peut direct choper l'orga car les Rdv on un organisation_id.

rdv.starts_at.in_time_zone(rdv.motif.organisation.time_zone)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cette implémentation continue de retourner à peu près la même valeur fausse, simplement sous une autre forme.

ActiveSupport::TimeWithZone#in_time_zone a pour effet de convertir un instant d'une timezone à une autre. Cela signifie que si un agent guadeloupéen prend un RDV à 14h30 aujourd'hui, nous stockons 2026-02-28 14:30 CET +01:00 en base, c'est à dire que nous stockons en réalité 2026-02-28 13:30 UTC.

Avec ce nouveau code, nous allons avoir cet effet :

Time.zone.parse("2026-02-28 14:30").in_time_zone("America/Guadeloupe").to_s
=> "2026-02-28 09:30:00 -0400"

Cette valeur correspond au même instant erroné que celui que l'API renvoie actuellement, juste formaté pour exprimer qu'il a lieu en -0400. Et donc le serveur de la coop va l'interpréter exactement de la même façon qu'actuellement, c'est à dire à 9h30 du matin en Guadeloupe.

Ce que nous voulons faire plutôt, c'est savoir que nous stockons en base une heure naïve, et nous le faisons en utilisant l'heure locale de Paris. Pour convertir cette heure naïve en heure locale guadeloupéenne, il faut l’interpréter naïvement dans la timezone guadeloupéenne :

ActiveSupport::TimeZone["America/Guadeloupe"].parse("2026-02-28 14:30").to_s
=> "2026-02-28 14:30:00 -0400"

Nous avons bien une représentation de l'instant auquel le RDV a lieu : aujourd'hui à 14h30 en Guadeloupe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tu as raison, je suis allé beaucoup trop vite sur le sujet, et j’ai pondu ce truc erroné.
J’étais biaisé parce que dans les ICS on avait « juste » changé la tzid, mais effectivement ici, il faudrait faire ce que tu proposes juste au-dessus idéalement.
Désolé d’avoir envoyé ce truc beaucoup trop vite
Je vais corriger quand j’aurai un peu de temps

else
rdv.starts_at
end
end

field :ends_at do |rdv, _options|
if rdv.motif.organisation.time_zone
rdv.ends_at.in_time_zone(rdv.motif.organisation.time_zone)
else
rdv.ends_at
end
end

# On permet des associations optionnelles, mais on les charge toutes si le paramètre `include` n'est pas utilisé
def self.conditional_association(association_name, blueprint_class)
field(association_name, if: ->(_field_name, _rdv, options) { !options["include"].is_a?(Array) || association_name.to_s.in?(options["include"]) }) do |rdv, _options|
Expand Down
19 changes: 19 additions & 0 deletions spec/requests/api/v1/rdvs_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,25 @@
expect(rdv).not_to have_key("organisation")
end
end

describe "utilise la bonne timezone" do
context "lorsque la timezone de l'organisation n'est pas définie" do
it "utilise la timezone de l'instance" do
get "/api/v1/rdvs", headers: headers, params: { user_id: user.id, agent_id: agent.id }, as: :json
expect(parsed_response_body["rdvs"].first["starts_at"]).to eq rdv_with_user_and_agent.starts_at.to_s
expect(parsed_response_body["rdvs"].first["ends_at"]).to eq rdv_with_user_and_agent.ends_at.to_s
end
end

context "lorsque la timezone de l'organisation est définie" do
let(:organisation) { create(:organisation, time_zone: "America/Guadeloupe") }

it "utilise la timezone de l'organisation" do
get "/api/v1/rdvs", headers: headers, params: { user_id: user.id, agent_id: agent.id }, as: :json
expect(parsed_response_body["rdvs"].first["ends_at"]).to eq rdv_with_user_and_agent.ends_at.in_time_zone("America/Guadeloupe").to_s
end
end
end
Comment on lines +107 to +125
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ces tests n'explicitent pas le comportement attendu, mais vérifient uniquement que l'on utilise in_time_zone.

Est-on d'accord que le comportement attendu est celui-ci ?

    describe "utilise la bonne timezone" do
      context "lorsque la timezone de l'organisation n'est pas définie" do
        it "utilise la timezone de l'instance" do
          rdv = create(:rdv, starts_at: ActiveSupport::TimeZone["Paris"].parse("2026-02-28 14:30"), organisation:, agents: [agent])
          get "/api/v1/rdvs", headers: headers, params: { agent_id: agent.id }, as: :json
          api_rdv = parsed_response_body["rdvs"].find { _1["id"].to_i == rdv.id }
          expect(api_rdv["starts_at"]).to eq "2026-02-28 14:30:00 +0100"
          expect(api_rdv["ends_at"]).to eq "2026-02-28 15:15:00 +0100"
        end
      end

      context "lorsque la timezone de l'organisation est définie" do
        let(:organisation) { create(:organisation, time_zone: "America/Guadeloupe") }

        it "utilise la timezone de l'organisation" do
          rdv = create(:rdv, starts_at: ActiveSupport::TimeZone["Paris"].parse("2026-02-28 14:30"), organisation:, agents: [agent])
          get "/api/v1/rdvs", headers: headers, params: { agent_id: agent.id }, as: :json
          api_rdv = parsed_response_body["rdvs"].find { _1["id"].to_i == rdv.id }
          expect(api_rdv["starts_at"]).to eq "2026-02-28 14:30:00 -0400"
          expect(api_rdv["ends_at"]).to eq "2026-02-28 15:15:00 -0400"
        end
      end
    end

end

describe "#create" do
Expand Down