From 10dc3b7ad5fbeb90ff6e0274d76c2be1bc097c08 Mon Sep 17 00:00:00 2001 From: Sai Date: Wed, 30 Jul 2025 00:35:35 +0800 Subject: [PATCH 1/2] added pharmacy --- controllers/pharmacy_handler.go | 240 ++++++++++++ controllers/prescription_handler.go | 141 +++++++ entities/drug.go | 61 +++ entities/errors.go | 2 + entities/prescription.go | 53 +++ main.go | 13 +- repository/postgres/postgres_pharmacy.go | 259 +++++++++++++ repository/postgres/postgres_prescriptions.go | 349 ++++++++++++++++++ sql/setup.sql | 61 +++ usecases/pharmacy_ucase.go | 101 +++++ usecases/prescription_ucase.go | 60 +++ 11 files changed, 1338 insertions(+), 2 deletions(-) create mode 100644 controllers/pharmacy_handler.go create mode 100644 controllers/prescription_handler.go create mode 100644 entities/drug.go create mode 100644 entities/prescription.go create mode 100644 repository/postgres/postgres_pharmacy.go create mode 100644 repository/postgres/postgres_prescriptions.go create mode 100644 usecases/pharmacy_ucase.go create mode 100644 usecases/prescription_ucase.go diff --git a/controllers/pharmacy_handler.go b/controllers/pharmacy_handler.go new file mode 100644 index 0000000..5fb1a18 --- /dev/null +++ b/controllers/pharmacy_handler.go @@ -0,0 +1,240 @@ +package controllers + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + "github.com/go-playground/validator/v10" + + "github.com/jieqiboh/sothea_backend/controllers/middleware" + "github.com/jieqiboh/sothea_backend/entities" +) + +// ----------------------------------------------------------------------------- +// Handler struct + constructor +// ----------------------------------------------------------------------------- + +type PharmacyHandler struct { + Usecase entities.PharmacyUseCase +} + +// NewPharmacyHandler registers /pharmacy/* routes and applies JWT auth. +func NewPharmacyHandler(r *gin.Engine, uc entities.PharmacyUseCase, secretKey []byte) { + h := &PharmacyHandler{Usecase: uc} + + // NewPharmacyHandler … + grp := r.Group("/pharmacy") + grp.Use(middleware.AuthRequired(secretKey)) + { + // DRUG CATALOG + grp.GET("/drugs", h.ListDrugs) + grp.POST("/drugs", h.CreateDrug) + grp.GET("/drugs/:id", h.GetDrug) + grp.PATCH("/drugs/:id", h.UpdateDrug) + grp.DELETE("/drugs/:id", h.DeleteDrug) + + // BATCHES + grp.GET("/batches", h.ListBatches) + grp.POST("/batches", h.CreateBatch) + grp.PATCH("/batches/:id", h.UpdateBatch) + grp.DELETE("/batches/:id", h.DeleteBatch) + } +} + +// ----------------------------------------------------------------------------- +// Drug endpoints +// ----------------------------------------------------------------------------- + +func (h *PharmacyHandler) ListDrugs(c *gin.Context) { + ctx := c.Request.Context() + + drugs, err := h.Usecase.ListDrugs(ctx) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, drugs) +} + +func (h *PharmacyHandler) CreateDrug(c *gin.Context) { + var d entities.Drug + if err := c.ShouldBindJSON(&d); err != nil { + handleBindErr(c, err) + return + } + + ctx := c.Request.Context() + drug, err := h.Usecase.CreateDrug(ctx, &d) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, drug) +} + +func (h *PharmacyHandler) GetDrug(c *gin.Context) { + // 1. Parse :id from path + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + ctx := c.Request.Context() + detail, err := h.Usecase.GetDrug(ctx, id) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + + c.JSON(http.StatusOK, detail) +} + +func (h *PharmacyHandler) UpdateDrug(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + var d entities.Drug + if err := c.ShouldBindJSON(&d); err != nil { + handleBindErr(c, err) + return + } + d.ID = id // ensure path param wins + + ctx := c.Request.Context() + drug, err := h.Usecase.CreateDrug(ctx, &d) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, drug) +} + +func (h *PharmacyHandler) DeleteDrug(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + ctx := c.Request.Context() + if err := h.Usecase.DeleteDrug(ctx, id); err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} + +// ----------------------------------------------------------------------------- +// Batch endpoints +// ----------------------------------------------------------------------------- + +func (h *PharmacyHandler) ListBatches(c *gin.Context) { + var drugIDPtr *int64 + if q := c.Query("drug_id"); q != "" { + val, err := strconv.ParseInt(q, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid drug_id"}) + return + } + drugIDPtr = &val + } + + ctx := c.Request.Context() + batches, err := h.Usecase.ListBatches(ctx, drugIDPtr) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + fmt.Println("hello") + fmt.Println(batches) + c.JSON(http.StatusOK, batches) +} + +func (h *PharmacyHandler) CreateBatch(c *gin.Context) { + var b entities.DrugBatch + if err := c.ShouldBindJSON(&b); err != nil { + handleBindErr(c, err) + return + } + + ctx := c.Request.Context() + id, err := h.Usecase.CreateBatch(ctx, &b) + if err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, gin.H{"id": id}) +} + +func (h *PharmacyHandler) UpdateBatch(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + var b entities.DrugBatch + if err := c.ShouldBindJSON(&b); err != nil { + handleBindErr(c, err) + return + } + b.ID = id + + ctx := c.Request.Context() + if err := h.Usecase.UpdateBatch(ctx, &b); err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusOK) +} + +func (h *PharmacyHandler) DeleteBatch(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + ctx := c.Request.Context() + if err := h.Usecase.DeleteBatch(ctx, id); err != nil { + c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} + +// ----------------------------------------------------------------------------- +// Helper functions (copy-style from PatientHandler) +// ----------------------------------------------------------------------------- + +func handleBindErr(c *gin.Context, err error) { + if ve, ok := err.(validator.ValidationErrors); ok && len(ve) > 0 { + c.JSON(http.StatusBadRequest, gin.H{"error": ve[0].Error()}) + return + } + if err.Error() == "EOF" { + c.JSON(http.StatusBadRequest, gin.H{"error": "request body is empty"}) + return + } + c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) +} + +func mapPhErr(err error) int { + if err == nil { + return http.StatusOK + } + switch err { + case entities.ErrInternalServerError: + return http.StatusInternalServerError + case entities.ErrDrugNameTaken: + return http.StatusConflict + default: + return http.StatusInternalServerError + } +} diff --git a/controllers/prescription_handler.go b/controllers/prescription_handler.go new file mode 100644 index 0000000..852cb62 --- /dev/null +++ b/controllers/prescription_handler.go @@ -0,0 +1,141 @@ +package controllers + +import ( + "fmt" + "net/http" + "strconv" + + "github.com/gin-gonic/gin" + + "github.com/jieqiboh/sothea_backend/controllers/middleware" + "github.com/jieqiboh/sothea_backend/entities" +) + +// ----------------------------------------------------------------------------- +// Handler struct + constructor +// ----------------------------------------------------------------------------- + +type PrescriptionHandler struct { + Usecase entities.PrescriptionUseCase +} + +func NewPrescriptionHandler(r *gin.Engine, uc entities.PrescriptionUseCase, secretKey []byte) { + h := &PrescriptionHandler{Usecase: uc} + + grp := r.Group("/prescriptions") + grp.Use(middleware.AuthRequired(secretKey)) + { + grp.GET("", h.ListPrescriptions) + grp.POST("", h.CreatePrescription) + grp.GET(":id", h.GetPrescription) + grp.PATCH(":id", h.UpdatePrescription) + grp.DELETE(":id", h.DeletePrescription) + } +} + +// ----------------------------------------------------------------------------- +// CRUD endpoints +// ----------------------------------------------------------------------------- + +func (h *PrescriptionHandler) ListPrescriptions(c *gin.Context) { + var patientIDPtr *int64 + var vidPtr *int32 + + if q := c.Query("patient_id"); q != "" { + val, err := strconv.ParseInt(q, 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid patient_id"}) + return + } + patientIDPtr = &val + } + + if q := c.Query("vid"); q != "" { + val, err := strconv.ParseInt(q, 10, 32) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid vid"}) + return + } + tmp := int32(val) + vidPtr = &tmp + } + + ctx := c.Request.Context() + prescriptions, err := h.Usecase.ListPrescriptions(ctx, patientIDPtr, vidPtr) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, prescriptions) +} + +func (h *PrescriptionHandler) CreatePrescription(c *gin.Context) { + var p entities.Prescription + if err := c.ShouldBindJSON(&p); err != nil { + handleBindErr(c, err) + return + } + + ctx := c.Request.Context() + prescription, err := h.Usecase.CreatePrescription(ctx, &p) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, prescription) +} + +func (h *PrescriptionHandler) GetPrescription(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + fmt.Println("HEHEHEHHE") + ctx := c.Request.Context() + prescription, err := h.Usecase.GetPrescriptionByID(ctx, id) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, prescription) +} + +func (h *PrescriptionHandler) UpdatePrescription(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + var p entities.Prescription + if err := c.ShouldBindJSON(&p); err != nil { + handleBindErr(c, err) + return + } + p.ID = id + + ctx := c.Request.Context() + prescription, err := h.Usecase.UpdatePrescription(ctx, &p) + if err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.JSON(http.StatusOK, prescription) +} + +func (h *PrescriptionHandler) DeletePrescription(c *gin.Context) { + id, err := strconv.ParseInt(c.Param("id"), 10, 64) + if err != nil { + c.JSON(http.StatusBadRequest, gin.H{"error": "invalid id"}) + return + } + + ctx := c.Request.Context() + if err := h.Usecase.DeletePrescription(ctx, id); err != nil { + c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) + return + } + c.Status(http.StatusNoContent) +} diff --git a/entities/drug.go b/entities/drug.go new file mode 100644 index 0000000..bbf4b3e --- /dev/null +++ b/entities/drug.go @@ -0,0 +1,61 @@ +// entities/drug.go +package entities + +import ( + "context" + "time" +) + +// Represents the "type" of drug (e.g. Benadryl) +type Drug struct { + ID int64 `json:"id"` + Name string `json:"name"` + Unit string `json:"unit"` + DefaultSize *int `json:"default_size,omitempty"` + Notes *string `json:"notes,omitempty"` +} + +// Represents each batch of the drug +type DrugBatch struct { + ID int64 `json:"id"` + DrugID int64 `json:"drug_id"` + BatchNumber string `json:"batch_no"` + Location *string `json:"location,omitempty"` + Quantity int `json:"quantity"` + ExpiryDate time.Time `json:"expiry_date"` + Supplier *string `json:"supplier,omitempty"` + DepletedAt *time.Time `json:"depleted_at,omitempty"` +} + +type DrugDetail struct { + Drug + Batches []DrugBatch `json:"batches"` +} + +type PharmacyRepository interface { + ListDrugs(ctx context.Context) ([]Drug, error) + CreateDrug(ctx context.Context, d *Drug) (*Drug, error) + GetDrug(ctx context.Context, id int64) (*Drug, error) + UpdateDrug(ctx context.Context, d *Drug) (*Drug, error) + DeleteDrug(ctx context.Context, id int64) error + + ListBatches(ctx context.Context, drugID *int64) ([]DrugBatch, error) // set drugID = nil for all batches + CreateBatch(ctx context.Context, b *DrugBatch) (int64, error) + UpdateBatch(ctx context.Context, b *DrugBatch) error + DeleteBatch(ctx context.Context, id int64) error +} + +type PharmacyUseCase interface { + // Drug-level + ListDrugs(ctx context.Context) ([]Drug, error) + CreateDrug(ctx context.Context, d *Drug) (*Drug, error) + GetDrug(ctx context.Context, id int64) (*DrugDetail, error) + UpdateDrug(ctx context.Context, d *Drug) (*Drug, error) + DeleteDrug(ctx context.Context, id int64) error + + // Batch-level + ListBatches(ctx context.Context, drugID *int64) ([]DrugBatch, error) // set drugID = nil for all batches + CreateBatch(ctx context.Context, b *DrugBatch) (int64, error) + UpdateBatch(ctx context.Context, b *DrugBatch) error + DeleteBatch(ctx context.Context, id int64) error +} diff --git a/entities/errors.go b/entities/errors.go index d81d626..8d9e8bc 100644 --- a/entities/errors.go +++ b/entities/errors.go @@ -9,4 +9,6 @@ var ( ErrMissingAdminCategory = errors.New("Missing Admin field") ErrAuthenticationFailed = errors.New("Not Authenticated") ErrLoginFailed = errors.New("Login Failed") + + ErrDrugNameTaken = errors.New("A drug with that name already exists") ) diff --git a/entities/prescription.go b/entities/prescription.go new file mode 100644 index 0000000..14de780 --- /dev/null +++ b/entities/prescription.go @@ -0,0 +1,53 @@ +package entities + +import ( + "context" + "time" +) + +type Prescription struct { + ID int64 `json:"id"` + VID int32 `json:"vid"` + PatientID int64 `json:"patientId"` + StaffID *int64 `json:"staffId"` // Optional + Notes *string `json:"notes"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + PrescribedDrugs []DrugPrescription `json:"prescribedDrugs"` +} + +type DrugPrescription struct { + ID int64 `json:"id"` + PrescriptionID int64 `json:"prescriptionId"` + DrugID int64 `json:"drugId"` + Quantity int `json:"quantity"` + Remarks *string `json:"remarks"` // aka instructions + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + Batches []PrescriptionBatchItem `json:"batches"` +} + +type PrescriptionBatchItem struct { + ID int64 `json:"id"` + DrugPrescriptionID int64 `json:"drugPrescriptionId"` + BatchId int64 `json:"batchId"` + Quantity int `json:"quantity"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` +} + +type PrescriptionRepository interface { + CreatePrescription(ctx context.Context, p *Prescription) (*Prescription, error) + GetPrescriptionByID(ctx context.Context, id int64) (*Prescription, error) + ListPrescriptions(ctx context.Context, patientID *int64, vid *int32) ([]*Prescription, error) + UpdatePrescription(ctx context.Context, p *Prescription) (*Prescription, error) + DeletePrescription(ctx context.Context, id int64) error +} + +type PrescriptionUseCase interface { + CreatePrescription(ctx context.Context, p *Prescription) (*Prescription, error) + GetPrescriptionByID(ctx context.Context, id int64) (*Prescription, error) + ListPrescriptions(ctx context.Context, patientID *int64, vid *int32) ([]*Prescription, error) + UpdatePrescription(ctx context.Context, p *Prescription) (*Prescription, error) + DeletePrescription(ctx context.Context, id int64) error +} diff --git a/main.go b/main.go index a9e3b55..cd90296 100644 --- a/main.go +++ b/main.go @@ -4,14 +4,15 @@ import ( "database/sql" "flag" "fmt" + "log" + "time" + "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" _httpDelivery "github.com/jieqiboh/sothea_backend/controllers" _patientPostgresRepository "github.com/jieqiboh/sothea_backend/repository/postgres" _useCase "github.com/jieqiboh/sothea_backend/usecases" "github.com/spf13/viper" - "log" - "time" ) func main() { @@ -87,5 +88,13 @@ func main() { patientUseCase := _useCase.NewPatientUsecase(patientRepo, 2*time.Second) _httpDelivery.NewPatientHandler(router, patientUseCase, secretKey) + pharmacyRepo := _patientPostgresRepository.NewPostgresPharmacyRepository(db) + pharmacyUseCase := _useCase.NewPharmacyUsecase(pharmacyRepo, 2*time.Second) + _httpDelivery.NewPharmacyHandler(router, pharmacyUseCase, secretKey) + + prescriptionRepo := _patientPostgresRepository.NewPostgresPrescriptionRepository(db) + prescriptionUseCase := _useCase.NewPrescriptionUsecase(prescriptionRepo, 2*time.Second) + _httpDelivery.NewPrescriptionHandler(router, prescriptionUseCase, secretKey) + router.Run(address) } diff --git a/repository/postgres/postgres_pharmacy.go b/repository/postgres/postgres_pharmacy.go new file mode 100644 index 0000000..2380f2a --- /dev/null +++ b/repository/postgres/postgres_pharmacy.go @@ -0,0 +1,259 @@ +package postgres + +import ( + "context" + "database/sql" + "errors" + + "github.com/jieqiboh/sothea_backend/entities" +) + +// ----------------------------------------------------------------------------- +// STRUCT + CONSTRUCTOR +// ----------------------------------------------------------------------------- + +type postgresPharmacyRepository struct { + Conn *sql.DB +} + +func NewPostgresPharmacyRepository(conn *sql.DB) entities.PharmacyRepository { + return &postgresPharmacyRepository{conn} +} + +// ----------------------------------------------------------------------------- +// DRUGS (catalog) +// ----------------------------------------------------------------------------- + +const qListDrugs = ` +SELECT id, name, unit, default_size, notes +FROM drugs +ORDER BY name;` + +func (r *postgresPharmacyRepository) ListDrugs(ctx context.Context) ([]entities.Drug, error) { + rows, err := r.Conn.QueryContext(ctx, qListDrugs) + if err != nil { + return nil, err + } + defer rows.Close() + + out := []entities.Drug{} + for rows.Next() { + var d entities.Drug + if err := rows.Scan(&d.ID, &d.Name, &d.Unit, &d.DefaultSize, &d.Notes); err != nil { + return nil, err + } + out = append(out, d) + } + return out, rows.Err() +} + +const qCreateDrug = ` +INSERT INTO drugs (name, unit, default_size, notes) +VALUES ($1,$2,$3,$4) +ON CONFLICT (name) DO NOTHING +RETURNING id;` + +func (r *postgresPharmacyRepository) CreateDrug(ctx context.Context, d *entities.Drug) (*entities.Drug, error) { + var id int64 + err := r.Conn.QueryRowContext(ctx, qCreateDrug, + d.Name, d.Unit, d.DefaultSize, d.Notes, + ).Scan(&id) + switch { + case err == sql.ErrNoRows: // duplicate → no row returned + return nil, entities.ErrDrugNameTaken + + case err != nil: // other DB error + return nil, err + + default: // success + return r.GetDrug(ctx, id) + } +} + +const qGetDrug = ` +SELECT id, name, unit, default_size, notes +FROM drugs +WHERE id=$1;` + +func (r *postgresPharmacyRepository) GetDrug(ctx context.Context, id int64) (*entities.Drug, error) { + var d entities.Drug + err := r.Conn.QueryRowContext(ctx, qGetDrug, id). + Scan(&d.ID, &d.Name, &d.Unit, &d.DefaultSize, &d.Notes) + if err == sql.ErrNoRows { + return nil, errors.New("drug not found") + } + return &d, err +} + +const qUpdateDrug = ` +UPDATE drugs +SET name=$2, + unit=$3, + default_size=$4, + notes=$5, + updated_at=NOW() +WHERE id=$1;` + +func (r *postgresPharmacyRepository) UpdateDrug( + ctx context.Context, d *entities.Drug) (*entities.Drug, error) { + + res, err := r.Conn.ExecContext(ctx, qUpdateDrug, + d.ID, d.Name, d.Unit, d.DefaultSize, d.Notes) + if err != nil { + return nil, err + } + aff, _ := res.RowsAffected() + if aff == 0 { + return nil, errors.New("drug not found") + } + return r.GetDrug(ctx, d.ID) +} + +const qDeleteDrug = `DELETE FROM drugs WHERE id=$1;` + +func (r *postgresPharmacyRepository) DeleteDrug( + ctx context.Context, id int64) error { + + res, err := r.Conn.ExecContext(ctx, qDeleteDrug, id) + if err != nil { + return err + } + aff, _ := res.RowsAffected() + if aff == 0 { + return errors.New("drug not found") + } + return nil +} + +// ----------------------------------------------------------------------------- +// DRUG BATCHES (stock entries) +// ----------------------------------------------------------------------------- + +func (r *postgresPharmacyRepository) ListBatches( + ctx context.Context, + drugID *int64, // nil = show all +) ([]entities.DrugBatch, error) { + + base := ` +SELECT id, drug_id, batch_no, location, + quantity, expiry_date, supplier, depleted_at +FROM drug_batches` + var rows *sql.Rows + var err error + + if drugID != nil { + rows, err = r.Conn.QueryContext(ctx, + base+" WHERE drug_id=$1 ORDER BY expiry_date", *drugID) + } else { + rows, err = r.Conn.QueryContext(ctx, + base+" ORDER BY drug_id, expiry_date") + } + if err != nil { + return nil, err + } + defer rows.Close() + + out := []entities.DrugBatch{} + for rows.Next() { + var b entities.DrugBatch + if err := rows.Scan(&b.ID, &b.DrugID, &b.BatchNumber, &b.Location, + &b.Quantity, &b.ExpiryDate, &b.Supplier, &b.DepletedAt); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} + +const qCreateBatch = ` +INSERT INTO drug_batches + (drug_id, batch_no, location, quantity, expiry_date, supplier) +VALUES ($1,$2,$3,$4,$5,$6) +RETURNING id;` + +func (r *postgresPharmacyRepository) CreateBatch(ctx context.Context, b *entities.DrugBatch) (int64, error) { + var id int64 + err := r.Conn.QueryRowContext(ctx, qCreateBatch, + b.DrugID, b.BatchNumber, b.Location, + b.Quantity, b.ExpiryDate, b.Supplier, + ).Scan(&id) + + return id, err +} + +const qUpdateBatch = ` +UPDATE drug_batches +SET drug_id=$2, + batch_no=$3, + location=$4, + quantity=$5, + expiry_date=$6, + supplier=$7, + depleted_at=$8, + updated_at=NOW() +WHERE id=$1;` + +func (r *postgresPharmacyRepository) UpdateBatch( + ctx context.Context, b *entities.DrugBatch) error { + + res, err := r.Conn.ExecContext(ctx, qUpdateBatch, + b.ID, b.DrugID, b.BatchNumber, b.Location, + b.Quantity, b.ExpiryDate, b.Supplier, b.DepletedAt) + if err != nil { + return err + } + aff, _ := res.RowsAffected() + if aff == 0 { + return errors.New("batch not found") + } + return nil +} + +const qDeleteBatch = `DELETE FROM drug_batches WHERE id=$1;` + +func (r *postgresPharmacyRepository) DeleteBatch( + ctx context.Context, id int64) error { + + res, err := r.Conn.ExecContext(ctx, qDeleteBatch, id) + if err != nil { + return err + } + aff, _ := res.RowsAffected() + if aff == 0 { + return errors.New("batch not found") + } + return nil +} + +// ----------------------------------------------------------------------------- +// (Optional) FEFO helper – not wired yet +// ----------------------------------------------------------------------------- + +func (r *postgresPharmacyRepository) earliestBatches( + ctx context.Context, drugID int64, +) ([]entities.DrugBatch, error) { + + const q = ` +SELECT id, drug_id, batch_no, location, + quantity, expiry_date, supplier, depleted_at +FROM drug_batches +WHERE drug_id = $1 AND quantity > 0 +ORDER BY expiry_date;` + + rows, err := r.Conn.QueryContext(ctx, q, drugID) + if err != nil { + return nil, err + } + defer rows.Close() + + var out []entities.DrugBatch + for rows.Next() { + var b entities.DrugBatch + if err := rows.Scan(&b.ID, &b.DrugID, &b.BatchNumber, &b.Location, + &b.Quantity, &b.ExpiryDate, &b.Supplier, &b.DepletedAt); err != nil { + return nil, err + } + out = append(out, b) + } + return out, rows.Err() +} diff --git a/repository/postgres/postgres_prescriptions.go b/repository/postgres/postgres_prescriptions.go new file mode 100644 index 0000000..c8bbecb --- /dev/null +++ b/repository/postgres/postgres_prescriptions.go @@ -0,0 +1,349 @@ +package postgres + +import ( + "context" + "database/sql" + "errors" + "fmt" + + "github.com/jieqiboh/sothea_backend/entities" +) + +type postgresPrescriptionRepository struct { + Conn *sql.DB +} + +func NewPostgresPrescriptionRepository(conn *sql.DB) entities.PrescriptionRepository { + return &postgresPrescriptionRepository{Conn: conn} +} + +// ----------------------------------------------------------------------------- +// PRESCRIPTIONS +// ----------------------------------------------------------------------------- + +func (r *postgresPrescriptionRepository) CreatePrescription(ctx context.Context, p *entities.Prescription) (*entities.Prescription, error) { + tx, err := r.Conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + fmt.Println("HELLO") + fmt.Println(p.PatientID) + fmt.Println(p.VID) + + err = tx.QueryRowContext(ctx, ` + INSERT INTO prescriptions (patient_id, vid, staff_id, notes) + VALUES ($1, $2, $3, $4) + RETURNING id, created_at, updated_at + `, p.PatientID, p.VID, p.StaffID, p.Notes). + Scan(&p.ID, &p.CreatedAt, &p.UpdatedAt) + if err != nil { + return nil, err + } + + for i := range p.PrescribedDrugs { + d := &p.PrescribedDrugs[i] + + err = tx.QueryRowContext(ctx, ` + INSERT INTO drug_prescriptions (prescription_id, drug_id, quantity, remarks) + VALUES ($1, $2, $3, $4) + RETURNING id, created_at, updated_at + `, p.ID, d.DrugID, d.Quantity, d.Remarks). + Scan(&d.ID, &d.CreatedAt, &d.UpdatedAt) + if err != nil { + return nil, err + } + + for j := range d.Batches { + b := &d.Batches[j] + + err = tx.QueryRowContext(ctx, ` + INSERT INTO prescription_batch_items (drug_prescription_id, drug_batch_id, quantity) + VALUES ($1, $2, $3) + RETURNING id, created_at, updated_at + `, d.ID, b.BatchId, b.Quantity). + Scan(&b.ID, &b.CreatedAt, &b.UpdatedAt) + if err != nil { + return nil, err + } + + res, err := tx.ExecContext(ctx, ` + UPDATE drug_batches + SET quantity = quantity - $1 + WHERE id = $2 AND quantity >= $1 + `, b.Quantity, b.BatchId) + if err != nil { + return nil, err + } + rows, _ := res.RowsAffected() + if rows == 0 { + return nil, fmt.Errorf("insufficient stock in batch %d", b.BatchId) + } + } + } + + if err := tx.Commit(); err != nil { + return nil, err + } + + return r.GetPrescriptionByID(ctx, p.ID) +} + +func (r *postgresPrescriptionRepository) GetPrescriptionByID(ctx context.Context, id int64) (*entities.Prescription, error) { + var p entities.Prescription + + fmt.Println("HELLO1") + fmt.Println(p.PrescribedDrugs) + err := r.Conn.QueryRowContext(ctx, ` + SELECT id, patient_id, vid, staff_id, notes, created_at, updated_at + FROM prescriptions + WHERE id = $1 + `, id).Scan(&p.ID, &p.PatientID, &p.VID, &p.StaffID, &p.Notes, &p.CreatedAt, &p.UpdatedAt) + if err != nil { + return nil, err + } + + fmt.Println("HELLO") + fmt.Println(p.PrescribedDrugs) + + drugRows, err := r.Conn.QueryContext(ctx, ` + SELECT id, prescription_id, drug_id, quantity, remarks, created_at, updated_at + FROM drug_prescriptions + WHERE prescription_id = $1 + `, id) + if err != nil { + return nil, err + } + defer drugRows.Close() + + for drugRows.Next() { + var d entities.DrugPrescription + err := drugRows.Scan(&d.ID, &d.PrescriptionID, &d.DrugID, &d.Quantity, &d.Remarks, &d.CreatedAt, &d.UpdatedAt) + if err != nil { + return nil, err + } + + batchRows, err := r.Conn.QueryContext(ctx, ` + SELECT id, drug_prescription_id, drug_batch_id, quantity, created_at, updated_at + FROM prescription_batch_items + WHERE drug_prescription_id = $1 + `, d.ID) + if err != nil { + return nil, err + } + defer batchRows.Close() + + for batchRows.Next() { + var b entities.PrescriptionBatchItem + var drugBatchID int64 + err := batchRows.Scan(&b.ID, &b.DrugPrescriptionID, &drugBatchID, &b.Quantity, &b.CreatedAt, &b.UpdatedAt) + if err != nil { + return nil, err + } + b.BatchId = drugBatchID + d.Batches = append(d.Batches, b) + } + p.PrescribedDrugs = append(p.PrescribedDrugs, d) + } + + return &p, nil +} + +func (r *postgresPrescriptionRepository) ListPrescriptions(ctx context.Context, patientID *int64, vid *int32) ([]*entities.Prescription, error) { + query := ` + SELECT id, patient_id, vid, staff_id, notes, created_at, updated_at + FROM prescriptions` + + var rows *sql.Rows + var err error + + switch { + case patientID != nil && vid != nil: + query += ` WHERE patient_id = $1 AND vid = $2 ORDER BY created_at DESC` + rows, err = r.Conn.QueryContext(ctx, query, *patientID, *vid) + case patientID != nil: + query += ` WHERE patient_id = $1 ORDER BY created_at DESC` + rows, err = r.Conn.QueryContext(ctx, query, *patientID) + default: + query += ` ORDER BY created_at DESC` + rows, err = r.Conn.QueryContext(ctx, query) + } + if err != nil { + return nil, err + } + defer rows.Close() + + var result []*entities.Prescription + for rows.Next() { + var p entities.Prescription + err := rows.Scan(&p.ID, &p.PatientID, &p.VID, &p.StaffID, &p.Notes, &p.CreatedAt, &p.UpdatedAt) + if err != nil { + return nil, err + } + + // Hydrate prescribed drugs + drugRows, err := r.Conn.QueryContext(ctx, ` + SELECT id, prescription_id, drug_id, quantity, remarks, created_at, updated_at + FROM drug_prescriptions + WHERE prescription_id = $1 + `, p.ID) + if err != nil { + return nil, err + } + + var prescribedDrugs []entities.DrugPrescription + for drugRows.Next() { + var d entities.DrugPrescription + err = drugRows.Scan(&d.ID, &d.PrescriptionID, &d.DrugID, &d.Quantity, &d.Remarks, &d.CreatedAt, &d.UpdatedAt) + if err != nil { + drugRows.Close() + return nil, err + } + + // Hydrate prescription batch items + batchRows, err := r.Conn.QueryContext(ctx, ` + SELECT id, drug_prescription_id, drug_batch_id, quantity, created_at, updated_at + FROM prescription_batch_items + WHERE drug_prescription_id = $1 + `, d.ID) + if err != nil { + drugRows.Close() + return nil, err + } + + var batches []entities.PrescriptionBatchItem + for batchRows.Next() { + var b entities.PrescriptionBatchItem + var drugBatchID int64 + err = batchRows.Scan(&b.ID, &b.DrugPrescriptionID, &drugBatchID, &b.Quantity, &b.CreatedAt, &b.UpdatedAt) + if err != nil { + batchRows.Close() + drugRows.Close() + return nil, err + } + b.BatchId = drugBatchID + batches = append(batches, b) + } + batchRows.Close() + + d.Batches = batches + prescribedDrugs = append(prescribedDrugs, d) + } + drugRows.Close() + + p.PrescribedDrugs = prescribedDrugs + result = append(result, &p) + } + + return result, nil +} + +func (r *postgresPrescriptionRepository) UpdatePrescription(ctx context.Context, p *entities.Prescription) (*entities.Prescription, error) { + tx, err := r.Conn.BeginTx(ctx, nil) + if err != nil { + return nil, err + } + defer tx.Rollback() + + // Step 1: Load existing prescription with drug + batch info + existing, err := r.GetPrescriptionByID(ctx, p.ID) + if err != nil { + return nil, err + } + + // Step 2: Restore stock for existing batch items + for _, d := range existing.PrescribedDrugs { + for _, b := range d.Batches { + _, err := tx.ExecContext(ctx, ` + UPDATE drug_batches + SET quantity = quantity + $1 + WHERE id = $2 + `, b.Quantity, b.BatchId) + if err != nil { + return nil, err + } + } + } + + // Step 3: Delete old batch items and drug prescriptions + _, err = tx.ExecContext(ctx, `DELETE FROM prescription_batch_items WHERE drug_prescription_id IN (SELECT id FROM drug_prescriptions WHERE prescription_id = $1)`, p.ID) + if err != nil { + return nil, err + } + _, err = tx.ExecContext(ctx, `DELETE FROM drug_prescriptions WHERE prescription_id = $1`, p.ID) + if err != nil { + return nil, err + } + + // Step 4: Update prescription metadata + _, err = tx.ExecContext(ctx, ` + UPDATE prescriptions + SET patient_id = $2, vid = $3, staff_id = $4, notes = $5, updated_at = now() + WHERE id = $1 + `, p.ID, p.PatientID, p.VID, p.StaffID, p.Notes) + if err != nil { + return nil, err + } + + // Step 5: Insert new prescribed drugs and batch items + for i := range p.PrescribedDrugs { + d := &p.PrescribedDrugs[i] + + err = tx.QueryRowContext(ctx, ` + INSERT INTO drug_prescriptions (prescription_id, drug_id, quantity, remarks) + VALUES ($1, $2, $3, $4) + RETURNING id, created_at, updated_at + `, p.ID, d.DrugID, d.Quantity, d.Remarks). + Scan(&d.ID, &d.CreatedAt, &d.UpdatedAt) + if err != nil { + return nil, err + } + + for j := range d.Batches { + b := &d.Batches[j] + + err = tx.QueryRowContext(ctx, ` + INSERT INTO prescription_batch_items (drug_prescription_id, drug_batch_id, quantity) + VALUES ($1, $2, $3) + RETURNING id, created_at, updated_at + `, d.ID, b.BatchId, b.Quantity). + Scan(&b.ID, &b.CreatedAt, &b.UpdatedAt) + if err != nil { + return nil, err + } + + // Step 6: Subtract new batch quantities + res, err := tx.ExecContext(ctx, ` + UPDATE drug_batches + SET quantity = quantity - $1 + WHERE id = $2 AND quantity >= $1 + `, b.Quantity, b.BatchId) + if err != nil { + return nil, err + } + rows, _ := res.RowsAffected() + if rows == 0 { + return nil, fmt.Errorf("insufficient stock in batch %d", b.BatchId) + } + } + } + + // Step 7: Commit + if err := tx.Commit(); err != nil { + return nil, err + } + + return r.GetPrescriptionByID(ctx, p.ID) +} + +func (r *postgresPrescriptionRepository) DeletePrescription(ctx context.Context, id int64) error { + res, err := r.Conn.ExecContext(ctx, `DELETE FROM prescriptions WHERE id = $1`, id) + if err != nil { + return err + } + rows, _ := res.RowsAffected() + if rows == 0 { + return errors.New("prescription not found") + } + return nil +} diff --git a/sql/setup.sql b/sql/setup.sql index 54b1185..850a381 100644 --- a/sql/setup.sql +++ b/sql/setup.sql @@ -196,3 +196,64 @@ -- INSERT INTO users (username, password_hash) VALUES ('dr18', '$2a$10$xJQIZNVos4y5LkYekDRHV.KeSgKnEzV71uYN3tYRZ3vaJoDPNGSBK'); -- INSERT INTO users (username, password_hash) VALUES ('dr19', '$2a$10$pvo3eKjP4ZFNx.bE1l9mwuFT87Wnm.FqDHvlJSFbmokP7DkYwy9Oq'); -- INSERT INTO users (username, password_hash) VALUES ('dr20', '$2a$10$7ZKQ56ckZ2Z9Jye1em62iOrKeMLggM5oI2V1Jpjw52Cmg4nKTmNOG'); + +-- Create Drug Catalog + CREATE TABLE IF NOT EXISTS drugs ( + id BIGSERIAL PRIMARY KEY, + name TEXT NOT NULL UNIQUE, + unit TEXT NOT NULL, + default_size INTEGER, + notes TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() +); + +-- Create Inventory Table + CREATE TABLE IF NOT EXISTS drug_batches ( + id BIGSERIAL PRIMARY KEY, + drug_id BIGINT NOT NULL REFERENCES drugs(id) ON DELETE CASCADE, + batch_no TEXT NOT NULL, + location TEXT, + notes TEXT, + quantity INTEGER NOT NULL CHECK (quantity >= 0), + expiry_date DATE NOT NULL, + supplier TEXT, + depleted_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + UNIQUE (drug_id, batch_no) +); + +CREATE TABLE prescriptions ( + id BIGSERIAL PRIMARY KEY, + patient_id INTEGER NOT NULL, + vid INTEGER NOT NULL, + staff_id INTEGER, + notes TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW(), + + CONSTRAINT fk_admin FOREIGN KEY (patient_id, vid) REFERENCES admin(id, vid) +); + +CREATE TABLE drug_prescriptions ( + id BIGSERIAL PRIMARY KEY, + prescription_id BIGINT NOT NULL REFERENCES prescriptions(id), + drug_id BIGINT NOT NULL REFERENCES drugs(id), + quantity INTEGER NOT NULL, + remarks TEXT, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + +CREATE TABLE prescription_batch_items ( + id BIGSERIAL PRIMARY KEY, + drug_prescription_id BIGINT NOT NULL REFERENCES drug_prescriptions(id), + drug_batch_id BIGINT NOT NULL REFERENCES drug_batches(id), + quantity INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT NOW(), + updated_at TIMESTAMP NOT NULL DEFAULT NOW() +); + + + diff --git a/usecases/pharmacy_ucase.go b/usecases/pharmacy_ucase.go new file mode 100644 index 0000000..1c0e971 --- /dev/null +++ b/usecases/pharmacy_ucase.go @@ -0,0 +1,101 @@ +package usecases + +import ( + "context" + "time" + + "github.com/jieqiboh/sothea_backend/entities" +) + +// ----------------------------------------------------------------------------- +// Struct + Constructor +// ----------------------------------------------------------------------------- + +type pharmacyUsecase struct { + repo entities.PharmacyRepository + contextTimeout time.Duration +} + +func NewPharmacyUsecase(r entities.PharmacyRepository, timeout time.Duration) entities.PharmacyUseCase { + return &pharmacyUsecase{ + repo: r, + contextTimeout: timeout, + } +} + +// ----------------------------------------------------------------------------- +// Drug-level methods +// ----------------------------------------------------------------------------- + +func (u *pharmacyUsecase) ListDrugs(ctx context.Context) ([]entities.Drug, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + + return u.repo.ListDrugs(ctx) +} + +func (u *pharmacyUsecase) CreateDrug(ctx context.Context, d *entities.Drug) (*entities.Drug, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + + return u.repo.CreateDrug(ctx, d) +} + +func (u *pharmacyUsecase) GetDrug(ctx context.Context, id int64) (*entities.DrugDetail, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + + drug, err := u.repo.GetDrug(ctx, id) + if err != nil { + return nil, err + } + + batches, err := u.repo.ListBatches(ctx, &id) + if err != nil { + return nil, err + } + + return &entities.DrugDetail{Drug: *drug, Batches: batches}, nil +} + +func (u *pharmacyUsecase) UpdateDrug(ctx context.Context, d *entities.Drug) (*entities.Drug, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.UpdateDrug(ctx, d) +} + +func (u *pharmacyUsecase) DeleteDrug(ctx context.Context, id int64) error { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.DeleteDrug(ctx, id) +} + +// ----------------------------------------------------------------------------- +// Batch-level methods +// ----------------------------------------------------------------------------- + +func (u *pharmacyUsecase) ListBatches(ctx context.Context, drugID *int64) ([]entities.DrugBatch, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + + return u.repo.ListBatches(ctx, drugID) +} + +func (u *pharmacyUsecase) CreateBatch(ctx context.Context, b *entities.DrugBatch) (int64, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + + return u.repo.CreateBatch(ctx, b) +} + +func (u *pharmacyUsecase) UpdateBatch(ctx context.Context, b *entities.DrugBatch) error { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.UpdateBatch(ctx, b) +} + +func (u *pharmacyUsecase) DeleteBatch(ctx context.Context, id int64) error { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.DeleteBatch(ctx, id) +} diff --git a/usecases/prescription_ucase.go b/usecases/prescription_ucase.go new file mode 100644 index 0000000..87e359d --- /dev/null +++ b/usecases/prescription_ucase.go @@ -0,0 +1,60 @@ +package usecases + +import ( + "context" + "fmt" + "time" + + "github.com/jieqiboh/sothea_backend/entities" +) + +// ----------------------------------------------------------------------------- +// Struct + Constructor +// ----------------------------------------------------------------------------- + +type prescriptionUsecase struct { + repo entities.PrescriptionRepository + contextTimeout time.Duration +} + +func NewPrescriptionUsecase(r entities.PrescriptionRepository, timeout time.Duration) entities.PrescriptionUseCase { + return &prescriptionUsecase{ + repo: r, + contextTimeout: timeout, + } +} + +// ----------------------------------------------------------------------------- +// Core Methods +// ----------------------------------------------------------------------------- + +func (u *prescriptionUsecase) CreatePrescription(ctx context.Context, p *entities.Prescription) (*entities.Prescription, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.CreatePrescription(ctx, p) +} + +func (u *prescriptionUsecase) GetPrescriptionByID(ctx context.Context, id int64) (*entities.Prescription, error) { + fmt.Print("TOEOEOEOEO") + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.GetPrescriptionByID(ctx, id) +} + +func (u *prescriptionUsecase) ListPrescriptions(ctx context.Context, patientID *int64, vid *int32) ([]*entities.Prescription, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.ListPrescriptions(ctx, patientID, vid) +} + +func (u *prescriptionUsecase) UpdatePrescription(ctx context.Context, p *entities.Prescription) (*entities.Prescription, error) { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.UpdatePrescription(ctx, p) +} + +func (u *prescriptionUsecase) DeletePrescription(ctx context.Context, id int64) error { + ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) + defer cancel() + return u.repo.DeletePrescription(ctx, id) +} From 09a92aca5080c00cb8b49819df762a6ba94667d9 Mon Sep 17 00:00:00 2001 From: Sai Date: Wed, 30 Jul 2025 00:36:16 +0800 Subject: [PATCH 2/2] lint --- controllers/pharmacy_handler.go | 4 +--- controllers/prescription_handler.go | 2 -- usecases/prescription_ucase.go | 2 -- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/controllers/pharmacy_handler.go b/controllers/pharmacy_handler.go index 5fb1a18..b935d46 100644 --- a/controllers/pharmacy_handler.go +++ b/controllers/pharmacy_handler.go @@ -1,7 +1,6 @@ package controllers import ( - "fmt" "net/http" "strconv" @@ -151,8 +150,7 @@ func (h *PharmacyHandler) ListBatches(c *gin.Context) { c.JSON(mapPhErr(err), gin.H{"error": err.Error()}) return } - fmt.Println("hello") - fmt.Println(batches) + c.JSON(http.StatusOK, batches) } diff --git a/controllers/prescription_handler.go b/controllers/prescription_handler.go index 852cb62..777a376 100644 --- a/controllers/prescription_handler.go +++ b/controllers/prescription_handler.go @@ -1,7 +1,6 @@ package controllers import ( - "fmt" "net/http" "strconv" @@ -92,7 +91,6 @@ func (h *PrescriptionHandler) GetPrescription(c *gin.Context) { return } - fmt.Println("HEHEHEHHE") ctx := c.Request.Context() prescription, err := h.Usecase.GetPrescriptionByID(ctx, id) if err != nil { diff --git a/usecases/prescription_ucase.go b/usecases/prescription_ucase.go index 87e359d..33c00fc 100644 --- a/usecases/prescription_ucase.go +++ b/usecases/prescription_ucase.go @@ -2,7 +2,6 @@ package usecases import ( "context" - "fmt" "time" "github.com/jieqiboh/sothea_backend/entities" @@ -35,7 +34,6 @@ func (u *prescriptionUsecase) CreatePrescription(ctx context.Context, p *entitie } func (u *prescriptionUsecase) GetPrescriptionByID(ctx context.Context, id int64) (*entities.Prescription, error) { - fmt.Print("TOEOEOEOEO") ctx, cancel := context.WithTimeout(ctx, u.contextTimeout) defer cancel() return u.repo.GetPrescriptionByID(ctx, id)