Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VisitNoteServicer.Find and return ErrBillExist on create if one already exists for a visit note #57

Merged
merged 1 commit into from
Dec 11, 2024
Merged
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
21 changes: 21 additions & 0 deletions bill.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,21 @@ package elation

import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"slices"
"time"

"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/trace"
)

const billExistError = "The visit note provided already has a bill associated with it."

var ErrBillExist = errors.New("bill already exists for visit note")

type BillServicer interface {
Create(ctx context.Context, create *BillCreate) (*Bill, *http.Response, error)
}
Expand Down Expand Up @@ -115,6 +122,20 @@ func (b *BillService) Create(ctx context.Context, create *BillCreate) (*Bill, *h
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "error making request")

var clientErr *Error
if errors.As(err, &clientErr) && clientErr.StatusCode == http.StatusBadRequest {
errorRes := map[string][]string{}
err := json.Unmarshal([]byte(clientErr.Body), &errorRes)
if err != nil {
return nil, res, fmt.Errorf("unmarshaling response body to error response: %w", err)
}

if len(errorRes["visit_note"]) > 0 && slices.Contains(errorRes["visit_note"], billExistError) {
return nil, res, ErrBillExist
}
}

return nil, res, fmt.Errorf("making request: %w", err)
}

Expand Down
50 changes: 50 additions & 0 deletions bill_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,53 @@ func TestBillService_Create(t *testing.T) {
})
}
}

func TestBillService_Create_already_exists(t *testing.T) {
assert := assert.New(t)

billCreate := &BillCreate{
ServiceLocation: 10,
VisitNote: 64409108504,
Patient: 64901939201,
Practice: 65540,
Physician: 64811630594,
}

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if tokenRequest(w, r) {
return
}

assert.Equal(http.MethodPost, r.Method)
assert.Equal("/bills", r.URL.Path)

body, err := io.ReadAll(r.Body)
assert.NoError(err)

actualBillCreate := &BillCreate{}
err = json.Unmarshal(body, actualBillCreate)
assert.NoError(err)

assert.Equal(billCreate, actualBillCreate)

errorRes := map[string][]string{
"visit_note": {billExistError},
}
b, err := json.Marshal(errorRes)
assert.NoError(err)

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusBadRequest)
//nolint
w.Write(b)
}))
defer srv.Close()

client := NewHTTPClient(srv.Client(), srv.URL+"/token", "", "", srv.URL)
svc := BillService{client}

created, res, err := svc.Create(context.Background(), billCreate)
assert.Nil(created)
assert.NotNil(res)
assert.ErrorIs(err, ErrBillExist)
}
34 changes: 34 additions & 0 deletions visit_note.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

type VisitNoteServicer interface {
Create(ctx context.Context, create *VisitNoteCreate) (*VisitNote, *http.Response, error)
Find(ctx context.Context, opts *FindVisitNotesOptions) (*Response[[]*VisitNote], *http.Response, error)
}

var _ VisitNoteServicer = (*VisitNoteService)(nil)
Expand Down Expand Up @@ -178,3 +179,36 @@ func (v *VisitNoteService) Create(ctx context.Context, create *VisitNoteCreate)

return vn, res, nil
}

type FindVisitNotesOptions struct {
*Pagination

Patient int64 `url:"patient,omitempty"`
Physician int64 `url:"physician,omitempty"`
Practice int64 `url:"practice,omitempty"`

LastModifiedGT time.Time `url:"last_modified_gt,omitempty"`
LastModifiedGTE time.Time `url:"last_modified_gte,omitempty"`
LastModifiedLT time.Time `url:"last_modified_lt,omitempty"`
LastModifiedLTE time.Time `url:"last_modified_lte,omitempty"`

FromSignedDate time.Time `url:"from_signed_date,omitempty"`
ToSignedDate time.Time `url:"to_signed_date,omitempty"`
Unsigned bool `url:"unsigned,omitempty"`
}

func (v *VisitNoteService) Find(ctx context.Context, opts *FindVisitNotesOptions) (*Response[[]*VisitNote], *http.Response, error) {
ctx, span := v.client.tracer.Start(ctx, "find visit notes", trace.WithSpanKind(trace.SpanKindClient))
defer span.End()

out := &Response[[]*VisitNote]{}

res, err := v.client.request(ctx, http.MethodGet, "/visit_notes", opts, nil, &out)
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "error making request")
return nil, res, fmt.Errorf("making request: %w", err)
}

return out, res, nil
}
92 changes: 92 additions & 0 deletions visit_note_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,95 @@ func TestVisitNoteService_Create(t *testing.T) {
})
}
}

func TestVisitNoteService_Find(t *testing.T) {
assert := assert.New(t)

opts := &FindVisitNotesOptions{
Patient: 123,
Physician: 456,
Practice: 789,
LastModifiedGT: time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC),
LastModifiedGTE: time.Date(2022, 1, 2, 0, 0, 0, 0, time.UTC),
LastModifiedLT: time.Date(2022, 1, 3, 0, 0, 0, 0, time.UTC),
LastModifiedLTE: time.Date(2022, 1, 4, 0, 0, 0, 0, time.UTC),
FromSignedDate: time.Date(2022, 1, 5, 0, 0, 0, 0, time.UTC),
ToSignedDate: time.Date(2022, 1, 6, 0, 0, 0, 0, time.UTC),
Unsigned: true,
}

visitNotes := []*VisitNote{
{
ID: 1,
},
{
ID: 2,
},
}

srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if tokenRequest(w, r) {
return
}

assert.Equal(http.MethodGet, r.Method)
assert.Equal("/visit_notes", r.URL.Path)

actualPatient := r.URL.Query().Get("patient")
assert.Equal(opts.Patient, strToInt64(actualPatient))

actualPhysician := r.URL.Query().Get("physician")
assert.Equal(opts.Physician, strToInt64(actualPhysician))

actualPractice := r.URL.Query().Get("practice")
assert.Equal(opts.Practice, strToInt64(actualPractice))

actualLastModifiedGT := r.URL.Query().Get("last_modified_gt")
assert.Equal(opts.LastModifiedGT.Format(time.RFC3339), actualLastModifiedGT)

actualLastModifiedGTE := r.URL.Query().Get("last_modified_gte")
assert.Equal(opts.LastModifiedGTE.Format(time.RFC3339), actualLastModifiedGTE)

actualLastModifiedLT := r.URL.Query().Get("last_modified_lt")
assert.Equal(opts.LastModifiedLT.Format(time.RFC3339), actualLastModifiedLT)

actualLastModifiedLTE := r.URL.Query().Get("last_modified_lte")
assert.Equal(opts.LastModifiedLTE.Format(time.RFC3339), actualLastModifiedLTE)

actualFromSignedDate := r.URL.Query().Get("from_signed_date")
assert.Equal(opts.FromSignedDate.Format(time.RFC3339), actualFromSignedDate)

actualToSignedDate := r.URL.Query().Get("to_signed_date")
assert.Equal(opts.ToSignedDate.Format(time.RFC3339), actualToSignedDate)

actualUnsigned := r.URL.Query().Get("unsigned")
assert.Equal(opts.Unsigned, strToBool(actualUnsigned))

b, err := json.Marshal(Response[[]*VisitNote]{
Results: []*VisitNote{
{
ID: 1,
},
{
ID: 2,
},
},
})
assert.NoError(err)

w.Header().Set("Content-Type", "application/json")
//nolint
w.Write(b)
}))
defer srv.Close()

client := NewHTTPClient(srv.Client(), srv.URL+"/token", "", "", srv.URL)
svc := VisitNoteService{client}

visitNotesRes, res, err := svc.Find(context.Background(), opts)
assert.NotEmpty(visitNotesRes)
assert.NotNil(res)
assert.NoError(err)

assert.Equal(visitNotes, visitNotesRes.Results)
}
Loading