From a88f9ea47b3ddceea6e4b21041e0304ff111d2f8 Mon Sep 17 00:00:00 2001
From: ccremer <github.account@chrigel.net>
Date: Wed, 9 Nov 2022 11:05:59 +0100
Subject: [PATCH] Fix saving timezone when updating a payslip

Without MarshalJSON for TimeZone, then the field
gets serialized as "{}", which Odoo accepts and then
also returns this when querying again, causing
UnmarshalJSON of the TimeZone to fail again.
---
 pkg/odoo/timezone.go      |  8 ++++++++
 pkg/odoo/timezone_test.go | 24 ++++++++++++++++++++++++
 2 files changed, 32 insertions(+)

diff --git a/pkg/odoo/timezone.go b/pkg/odoo/timezone.go
index d7df51b..c4777ef 100644
--- a/pkg/odoo/timezone.go
+++ b/pkg/odoo/timezone.go
@@ -27,6 +27,14 @@ func (tz *TimeZone) UnmarshalJSON(b []byte) error {
 	return nil
 }
 
+// MarshalJSON implements json.Marshaler.
+func (tz *TimeZone) MarshalJSON() ([]byte, error) {
+	if tz.IsEmpty() || tz.Location() == time.Local {
+		return []byte(`null`), nil
+	}
+	return []byte(fmt.Sprintf(`"%s"`, tz.loc)), nil
+}
+
 func (tz *TimeZone) Location() *time.Location {
 	if tz == nil {
 		return nil
diff --git a/pkg/odoo/timezone_test.go b/pkg/odoo/timezone_test.go
index f952224..9f13ce5 100644
--- a/pkg/odoo/timezone_test.go
+++ b/pkg/odoo/timezone_test.go
@@ -35,6 +35,30 @@ func TestTimeZone_UnmarshalJSON(t *testing.T) {
 	}
 }
 
+func TestTimeZone_MarshalJSON(t *testing.T) {
+	tests := map[string]struct {
+		givenLocation  *time.Location
+		expectedOutput []byte
+	}{
+		"nil":          {givenLocation: nil, expectedOutput: []byte("null")},
+		"EuropeZurich": {givenLocation: mustLoadLocation("Europe/Zurich"), expectedOutput: []byte(`"Europe/Zurich"`)},
+		"Local":        {givenLocation: time.Local, expectedOutput: []byte("null")}, // "Local" isn't recognized by Odoo
+		"UTC":          {givenLocation: time.UTC, expectedOutput: []byte(`"UTC"`)},
+	}
+	for name, tt := range tests {
+		t.Run(name, func(t *testing.T) {
+			subject := &TimeZone{loc: tt.givenLocation}
+			result, err := subject.MarshalJSON()
+			require.NoError(t, err)
+			if tt.expectedOutput == nil {
+				assert.Nil(t, result)
+			} else {
+				assert.Equal(t, tt.expectedOutput, result)
+			}
+		})
+	}
+}
+
 func mustLoadLocation(name string) *time.Location {
 	loc, err := time.LoadLocation(name)
 	if err != nil {