From 3f00233e3d96a223762064bf921b698042e6f16f Mon Sep 17 00:00:00 2001 From: Will Edgington Date: Tue, 10 Dec 2024 16:25:27 -0500 Subject: [PATCH] Add VisitNoteServicer.Find and return ErrBillExist on create if one already exists for a visit note --- bill.go | 21 +++++++++++ bill_test.go | 50 +++++++++++++++++++++++++ visit_note.go | 34 +++++++++++++++++ visit_note_test.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+) diff --git a/bill.go b/bill.go index c536ec5..a45514d 100644 --- a/bill.go +++ b/bill.go @@ -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) } @@ -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) } diff --git a/bill_test.go b/bill_test.go index 9d67996..e6d8997 100644 --- a/bill_test.go +++ b/bill_test.go @@ -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) +} diff --git a/visit_note.go b/visit_note.go index 6091e12..d93ed83 100644 --- a/visit_note.go +++ b/visit_note.go @@ -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) @@ -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 +} diff --git a/visit_note_test.go b/visit_note_test.go index e9fa853..b8bae96 100644 --- a/visit_note_test.go +++ b/visit_note_test.go @@ -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) +}