aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimen A. W. Olsen <hello@simenandre.no>2026-10-17 14:27:46 +0200
committerSimen A. W. Olsen <hello@simenandre.no>2026-10-17 14:27:46 +0200
commit08b6286f1bfe3892d0d63726d79cd5f1d9acac01 (patch)
treedc979b1836bfb57253c9837e321445a07d4413e8
parentc0a01ac65bb492640b169274614e0d8022f5076d (diff)
downloadfiken-08b6286f1bfe3892d0d63726d79cd5f1d9acac01.tar.gz
fiken-08b6286f1bfe3892d0d63726d79cd5f1d9acac01.zip
Add CreateContactv0.2.0
Needed to link an order to Fiken without leaving the terminal: the signed order already carries the legal name, organisation number and address, so the contact can be made from it.
-rw-r--r--fiken/fiken.go38
-rw-r--r--fiken/fiken_test.go35
2 files changed, 73 insertions, 0 deletions
diff --git a/fiken/fiken.go b/fiken/fiken.go
index 3179a62..9b46607 100644
--- a/fiken/fiken.go
+++ b/fiken/fiken.go
@@ -137,6 +137,44 @@ type Contact struct {
Inactive bool `json:"inactive"`
}
+// NewContact is a contact to create. Fiken requires only the name.
+type NewContact struct {
+ Name string `json:"name"`
+ Email string `json:"email,omitempty"`
+ OrganizationNumber string `json:"organizationNumber,omitempty"`
+ Customer bool `json:"customer"`
+ Supplier bool `json:"supplier"`
+ Address *Address `json:"address,omitempty"`
+}
+
+// Address is a postal address. Fiken requires a country on one.
+type Address struct {
+ StreetAddress string `json:"streetAddress,omitempty"`
+ PostCode string `json:"postCode,omitempty"`
+ City string `json:"city,omitempty"`
+ Country string `json:"country"`
+}
+
+// CreateContact creates a contact and returns its id, which Fiken reports
+// only in a Location header.
+func (c *Client) CreateContact(ctx context.Context, companySlug string, contact NewContact) (int64, error) {
+ assert.True(companySlug != "", "company slug must not be empty")
+ assert.True(strings.TrimSpace(contact.Name) != "", "a contact needs a name")
+
+ body, err := json.Marshal(contact)
+ if err != nil {
+ return 0, fmt.Errorf("encode contact: %w", err)
+ }
+ resp, err := c.do(ctx, http.MethodPost, "/companies/"+companySlug+"/contacts", nil, body)
+ if err != nil {
+ return 0, err
+ }
+ defer resp.Body.Close()
+ io.Copy(io.Discard, io.LimitReader(resp.Body, maxBodyBytes)) //nolint:errcheck
+
+ return idFromLocation(resp.Header.Get("Location"))
+}
+
// DraftLine is one line of an invoice draft. UnitPrice is in the invoice
// currency's minor unit — øre, cents — and Quantity is normally 1, so the
// amount is carried entirely by the price and no float ever enters the
diff --git a/fiken/fiken_test.go b/fiken/fiken_test.go
index 2962e4a..1f24b86 100644
--- a/fiken/fiken_test.go
+++ b/fiken/fiken_test.go
@@ -318,3 +318,38 @@ func TestCreditNoteWithoutNumberingIsRecognised(t *testing.T) {
t.Errorf("counter = %v, want the number before the first", counter)
}
}
+
+func TestCreateContactReadsTheLocationHeader(t *testing.T) {
+ var body NewContact
+ var path string
+ c := newTestClient(t, func(w http.ResponseWriter, r *http.Request) {
+ path = r.URL.Path
+ if r.Method != http.MethodPost {
+ t.Errorf("method = %s, want POST", r.Method)
+ }
+ if err := json.NewDecoder(r.Body).Decode(&body); err != nil {
+ t.Errorf("decode body: %v", err)
+ }
+ w.Header().Set("Location", "https://api.fiken.no/api/v2/companies/sokkel-as/contacts/9474586519")
+ w.WriteHeader(http.StatusCreated)
+ })
+
+ id, err := c.CreateContact(context.Background(), "sokkel-as", NewContact{
+ Name: "Upstacked AS", OrganizationNumber: "926030396", Customer: true,
+ Address: &Address{StreetAddress: "Drammensveien 151", PostCode: "0277", City: "Oslo", Country: "Norge"},
+ })
+ if err != nil {
+ t.Fatalf("CreateContact: %v", err)
+ }
+ if id != 9474586519 {
+ t.Errorf("contact id = %d, want 9474586519", id)
+ }
+ if !strings.HasSuffix(path, "/companies/sokkel-as/contacts") {
+ t.Errorf("posted to %s, want the company's contacts", path)
+ }
+ // Fiken distinguishes a customer from a supplier, and a contact created as
+ // neither cannot be invoiced.
+ if !body.Customer || body.Address == nil || body.Address.Country == "" {
+ t.Errorf("contact was sent as %+v, want a customer with a country on its address", body)
+ }
+}