first commit

This commit is contained in:
ryan
2026-02-06 14:22:35 +07:00
commit bd70440c71
185 changed files with 69518 additions and 0 deletions
+96
View File
@@ -0,0 +1,96 @@
package services
import (
"backendcareit/models"
"fmt"
"log"
"gorm.io/gorm"
)
func Edit_INACBG_Admin(db *gorm.DB, input models.Edit_INACBG_Request) error {
log.Printf("[Edit INACBG] Received ID_Billing=%d, Tipe=%s, Kode_count=%d, Delete_count=%d, Total_Klaim=%.2f, Billing_Sign=%s\n",
input.ID_Billing, input.Tipe_inacbg, len(input.Kode_inacbg), len(input.Kode_Delete), input.Total_Klaim, input.Billing_Sign)
tx := db.Begin()
if tx.Error != nil {
return tx.Error
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 1. Hapus kode yang udah dipilih untuk dihapus
if len(input.Kode_Delete) > 0 {
switch input.Tipe_inacbg {
case "RI":
if err := tx.Where("\"ID_Billing\" = ? AND \"ID_INACBG_RI\" IN ?", input.ID_Billing, input.Kode_Delete).Delete(&models.Billing_INACBG_RI{}).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal delete INACBG RI: %w", err)
}
case "RJ":
if err := tx.Where("\"ID_Billing\" = ? AND \"ID_INACBG_RJ\" IN ?", input.ID_Billing, input.Kode_Delete).Delete(&models.Billing_INACBG_RJ{}).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal delete INACBG RJ: %w", err)
}
default:
tx.Rollback()
return fmt.Errorf("invalid tipe_inacbg: %s", input.Tipe_inacbg)
}
}
// 2. Tambahin kode INACBG yang baru
if len(input.Kode_inacbg) > 0 {
switch input.Tipe_inacbg {
case "RI":
for _, kode := range input.Kode_inacbg {
inacbgRI := models.Billing_INACBG_RI{
ID_Billing: input.ID_Billing,
Kode_INACBG: kode,
}
if err := tx.Create(&inacbgRI).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal insert INACBG RI kode %s: %w", kode, err)
}
}
case "RJ":
for _, kode := range input.Kode_inacbg {
inacbgRJ := models.Billing_INACBG_RJ{
ID_Billing: input.ID_Billing,
Kode_INACBG: kode,
}
if err := tx.Create(&inacbgRJ).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal insert INACBG RJ kode %s: %w", kode, err)
}
}
}
}
// 3. Update data di tabel billing_pasien
updateData := map[string]interface{}{
"Total_Klaim": input.Total_Klaim,
"Billing_Sign": input.Billing_Sign,
}
if err := tx.Model(&models.BillingPasien{}).Where("\"ID_Billing\" = ?", input.ID_Billing).Updates(updateData).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal update billing_pasien: %w", err)
}
if err := tx.Commit().Error; err != nil {
return err
}
// 4. Ngirim email kalo billing_sign gak kosong
go func(id int) {
if err := SendEmailBillingSignToDokter(id); err != nil {
log.Printf("Warning: Gagal mengirim email ke dokter untuk billing ID %d: %v\n", id, err)
}
}(input.ID_Billing)
return nil
}
+173
View File
@@ -0,0 +1,173 @@
package services
import (
"errors"
"fmt"
"log"
"time"
"backendcareit/database"
"backendcareit/models"
"gorm.io/gorm"
)
// EditPasienComplete - Update data identitas pasien dalam billing (nama, umur, ruangan, dll) terus dengan lookup kode
func EditPasienComplete(billingId int, namaPasien string, usia int, jeniKelamin string, ruangan string, kelas string, tindakan []string, icd9 []string, icd10 []string, billingSign *string, totalTarifRS *float64) error {
log.Printf("[EditPasien] START - billingId:%d, nama:%s, tindakan_count:%d, icd9_count:%d, icd10_count:%d\n", billingId, namaPasien, len(tindakan), len(icd9), len(icd10))
// Get billing
var billing models.BillingPasien
if err := database.DB.Where("\"ID_Billing\" = ?", billingId).First(&billing).Error; err != nil {
log.Printf("[EditPasien] ERROR - billing not found: %v\n", err)
return errors.New("billing tidak ditemukan")
}
log.Printf("[EditPasien] ✓ Billing found - ID_Pasien: %d\n", billing.ID_Pasien)
// Start transaction
tx := database.DB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// Update pasien data
if err := tx.Model(&models.Pasien{}).
Where("\"ID_Pasien\" = ?", billing.ID_Pasien).
Updates(map[string]interface{}{
"Nama_Pasien": namaPasien,
"Usia": usia,
"Jenis_Kelamin": jeniKelamin,
"Ruangan": ruangan,
"Kelas": kelas,
}).Error; err != nil {
tx.Rollback()
return errors.New("gagal update data pasien: " + err.Error())
}
// Delete existing tindakan
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_Tindakan{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete tindakan: " + err.Error())
}
// Insert new tindakan dengan lookup berdasarkan nama tindakan
now := time.Now()
for _, tindakanNama := range tindakan {
if tindakanNama != "" {
log.Printf("[EditPasien] Looking up tindakan: '%s'\n", tindakanNama)
// Lookup tarif by deskripsi (nama tindakan) - use quoted column name for PostgreSQL
var tarif models.TarifRS
if err := tx.Where("\"Tindakan_RS\" = ?", tindakanNama).First(&tarif).Error; err != nil {
log.Printf("[EditPasien] ERROR - tindakan lookup failed: %v\n", err)
if errors.Is(err, gorm.ErrRecordNotFound) {
tx.Rollback()
log.Printf("[EditPasien] ERROR - tindakan '%s' not found in tarif_rs\n", tindakanNama)
return fmt.Errorf("tindakan '%s' tidak ditemukan", tindakanNama)
}
tx.Rollback()
return errors.New("gagal lookup tindakan: " + err.Error())
}
log.Printf("[EditPasien] ✓ Tindakan found - ID: %s, Harga: %d\n", tarif.KodeRS, tarif.Harga)
newTindakan := models.Billing_Tindakan{
ID_Billing: billingId,
ID_Tarif_RS: tarif.KodeRS,
Tanggal_Tindakan: &now,
}
if err := tx.Create(&newTindakan).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert tindakan: " + err.Error())
}
}
}
// Delete existing ICD9
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_ICD9{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete ICD9: " + err.Error())
}
// Insert new ICD9 dengan lookup berdasarkan nama prosedur
for _, icd9Nama := range icd9 {
if icd9Nama != "" {
// Lookup ICD9 by prosedur name
var icd9Data models.ICD9
if err := tx.Where("\"Prosedur\" = ?", icd9Nama).First(&icd9Data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
tx.Rollback()
return fmt.Errorf("ICD9 '%s' tidak ditemukan", icd9Nama)
}
tx.Rollback()
return errors.New("gagal lookup ICD9: " + err.Error())
}
newICD9 := models.Billing_ICD9{
ID_Billing: billingId,
ID_ICD9: icd9Data.Kode_ICD9,
}
if err := tx.Create(&newICD9).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert ICD9: " + err.Error())
}
}
}
// Delete existing ICD10
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_ICD10{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete ICD10: " + err.Error())
}
// Insert new ICD10 dengan lookup berdasarkan nama diagnosa
for _, icd10Nama := range icd10 {
if icd10Nama != "" {
// Lookup ICD10 by diagnosa name
var icd10Data models.ICD10
if err := tx.Where("\"Diagnosa\" = ?", icd10Nama).First(&icd10Data).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
tx.Rollback()
return fmt.Errorf("ICD10 '%s' tidak ditemukan", icd10Nama)
}
tx.Rollback()
return errors.New("gagal lookup ICD10: " + err.Error())
}
newICD10 := models.Billing_ICD10{
ID_Billing: billingId,
ID_ICD10: icd10Data.Kode_ICD10,
}
if err := tx.Create(&newICD10).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert ICD10: " + err.Error())
}
}
}
// Update billing_sign jika dikirimkan dari FE
if billingSign != nil {
if err := tx.Model(&models.BillingPasien{}).
Where("\"ID_Billing\" = ?", billingId).
Update("Billing_Sign", *billingSign).Error; err != nil {
tx.Rollback()
return errors.New("gagal update billing_sign: " + err.Error())
}
}
// Update total_tarif_rs jika dikirimkan dari FE
if totalTarifRS != nil {
if err := tx.Model(&models.BillingPasien{}).
Where("\"ID_Billing\" = ?", billingId).
Update("Total_Tarif_RS", *totalTarifRS).Error; err != nil {
tx.Rollback()
return errors.New("gagal update total_tarif_rs: " + err.Error())
}
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
return errors.New("gagal commit transaction: " + err.Error())
}
return nil
}
@@ -0,0 +1,403 @@
package services
import (
"errors"
"fmt"
"log"
"strings"
"time"
"backendcareit/models"
"gorm.io/gorm"
)
func Post_INACBG_Admin(db *gorm.DB, input models.Post_INACBG_Admin) error {
// Debug log
log.Printf("[INACBG] Input received: ID_Billing=%d, Tipe=%s, Kode_count=%d, Total_klaim=%.2f, BillingSign=%s\n",
input.ID_Billing, input.Tipe_inacbg, len(input.Kode_INACBG), input.Total_klaim, input.Billing_sign)
tx := db.Begin()
if tx.Error != nil {
log.Printf("[INACBG] Error starting transaction: %v\n", tx.Error)
return tx.Error
}
// Ensure rollback on panic / unexpected error
defer func() {
if r := recover(); r != nil {
log.Printf("[INACBG] Panic recovered: %v\n", r)
tx.Rollback()
}
}()
// Validate input
if input.Tipe_inacbg != "RI" && input.Tipe_inacbg != "RJ" {
tx.Rollback()
err := errors.New("invalid tipe_inacbg: must be 'RI' or 'RJ'")
log.Printf("[INACBG] Validation error: %v\n", err)
return err
}
if len(input.Kode_INACBG) == 0 {
tx.Rollback()
err := errors.New("Kode_INACBG tidak boleh kosong")
log.Printf("[INACBG] Validation error: %v\n", err)
return err
}
// Ngambil billing dulu buat dapetin total klaim yang lama
var existingBilling models.BillingPasien
if err := tx.First(&existingBilling, input.ID_Billing).Error; err != nil {
tx.Rollback()
if errors.Is(err, gorm.ErrRecordNotFound) {
err = fmt.Errorf("billing dengan ID_Billing=%d tidak ditemukan", input.ID_Billing)
log.Printf("[INACBG] %v\n", err)
return err
}
log.Printf("[INACBG] Error fetching billing: %v\n", err)
return fmt.Errorf("gagal mengambil billing: %w", err)
}
log.Printf("[INACBG] Found billing: ID=%d, Current_Total_Klaim=%.2f\n", existingBilling.ID_Billing, existingBilling.Total_Klaim)
// Hitung total klaim yang baru = yang lama + tambahan
newTotalKlaim := input.Total_klaim
log.Printf("[INACBG] New total klaim: %.2f + %.2f = %.2f\n", existingBilling.Total_Klaim, input.Total_klaim, newTotalKlaim)
// Parse Tanggal_Keluar jika diisi oleh admin
var keluarPtr *time.Time
if input.Tanggal_keluar != "" && input.Tanggal_keluar != "null" {
s := input.Tanggal_keluar
var parsed time.Time
var err error
layouts := []string{time.RFC3339, "2006-01-02 15:04:05", "2006-01-02"}
for _, layout := range layouts {
parsed, err = time.Parse(layout, s)
if err == nil {
t := parsed
keluarPtr = &t
log.Printf("[INACBG] Parsed tanggal_keluar: %v\n", t)
break
}
}
if keluarPtr == nil {
tx.Rollback()
err := fmt.Errorf("invalid tanggal_keluar format: %s", input.Tanggal_keluar)
log.Printf("[INACBG] %v\n", err)
return err
}
}
// Update total klaim kumulatif sama tanggal keluar (kalo ada yang ngirim)
updateData := map[string]interface{}{
"\"Total_Klaim\"": newTotalKlaim,
}
if keluarPtr != nil {
updateData["\"Tanggal_Keluar\""] = keluarPtr
}
// Kalo frontend kirim billing_sign, langsung simpen ke kolom Billing_Sign
if input.Billing_sign != "" {
updateData["\"Billing_Sign\""] = input.Billing_sign
log.Printf("[INACBG] Will update Billing_Sign to: %s\n", input.Billing_sign)
}
log.Printf("[INACBG] Update data: %v\n", updateData)
res := tx.Model(&models.BillingPasien{}).
Where("\"ID_Billing\" = ?", input.ID_Billing).
Updates(updateData)
if res.Error != nil {
tx.Rollback()
log.Printf("[INACBG] Error updating billing: %v\n", res.Error)
return fmt.Errorf("gagal update billing: %w", res.Error)
}
log.Printf("[INACBG] Updated %d rows in billing_pasien\n", res.RowsAffected)
// DELETE semua kode INACBG yang lama buat billing ini (biar gak duplikat pas INSERT)
switch input.Tipe_inacbg {
case "RI":
if err := tx.Where("\"ID_Billing\" = ?", input.ID_Billing).Delete(&models.Billing_INACBG_RI{}).Error; err != nil {
tx.Rollback()
log.Printf("[INACBG] Error deleting old INACBG RI: %v\n", err)
return fmt.Errorf("gagal delete INACBG RI lama: %w", err)
}
log.Printf("[INACBG] Deleted old INACBG RI records for ID_Billing=%d\n", input.ID_Billing)
case "RJ":
if err := tx.Where("\"ID_Billing\" = ?", input.ID_Billing).Delete(&models.Billing_INACBG_RJ{}).Error; err != nil {
tx.Rollback()
log.Printf("[INACBG] Error deleting old INACBG RJ: %v\n", err)
return fmt.Errorf("gagal delete INACBG RJ lama: %w", err)
}
log.Printf("[INACBG] Deleted old INACBG RJ records for ID_Billing=%d\n", input.ID_Billing)
}
// Bulk insert kode INACBG berdasarkan tipenya (udah dihapus yang lama)
switch input.Tipe_inacbg {
case "RI":
records := make([]models.Billing_INACBG_RI, 0, len(input.Kode_INACBG))
for _, kode := range input.Kode_INACBG {
records = append(records, models.Billing_INACBG_RI{
ID_Billing: input.ID_Billing,
Kode_INACBG: kode,
})
}
if err := tx.Create(&records).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal insert INACBG RI: %w", err)
}
case "RJ":
records := make([]models.Billing_INACBG_RJ, 0, len(input.Kode_INACBG))
for _, kode := range input.Kode_INACBG {
records = append(records, models.Billing_INACBG_RJ{
ID_Billing: input.ID_Billing,
Kode_INACBG: kode,
})
}
if err := tx.Create(&records).Error; err != nil {
tx.Rollback()
return fmt.Errorf("gagal insert INACBG RJ: %w", err)
}
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
log.Printf("[INACBG] Error committing transaction: %v\n", err)
return err
}
log.Printf("[INACBG] ✅ Successfully saved INACBG for ID_Billing=%d, billing_sign=%s\n", input.ID_Billing, input.Billing_sign)
// Ngirim email ke dokter kalo billing_sign gak kosong
if input.Billing_sign != "" && strings.TrimSpace(input.Billing_sign) != "" {
// Ngirim email asynchronous (kalo gagal, jangan perpengaruh proses utama)
// Log error tapi jangan return, biar proses utama tetep berhasil
if err := SendEmailBillingSignToDokter(input.ID_Billing); err != nil {
// Log error tapi tidak return error agar proses utama tetap berhasil
// Di production, bisa pake logger yang lebih proper
fmt.Printf("Warning: Gagal mengirim email ke dokter untuk billing ID %d: %v\n", input.ID_Billing, err)
}
}
return nil
}
func GetAllBilling(db *gorm.DB) ([]models.Request_Admin_Inacbg, error) {
var billings []models.BillingPasien
// Ngambil semua billing yang belum ditutup (Tanggal_Keluar masih kosong)
if err := db.Where("\"Tanggal_Keluar\" IS NULL").Find(&billings).Error; err != nil {
return nil, err
}
// Kumpulin dulu semua ID_Billing sama ID_Pasien
var billingIDs []int
var pasienIDs []int
for _, b := range billings {
billingIDs = append(billingIDs, b.ID_Billing)
pasienIDs = append(pasienIDs, b.ID_Pasien)
}
// Ambil pasien yang ada di billing aja
pasienMap := make(map[int]models.Pasien)
var pasienList []models.Pasien
if err := db.Where("\"ID_Pasien\" IN ?", pasienIDs).Find(&pasienList).Error; err != nil {
return nil, err
}
log.Printf("[DEBUG] Loaded %d pasien from database\n", len(pasienList))
for _, p := range pasienList {
pasienMap[p.ID_Pasien] = p
log.Printf("[DEBUG] Pasien %d: Nama=%s, Ruangan=%s\n", p.ID_Pasien, p.Nama_Pasien, p.Ruangan)
}
// Ambil tindakan yang berkaitan sama billing-billing ini
tindakanMap := make(map[int][]string)
var tindakanRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanRows {
tindakanMap[t.ID_Billing] = append(tindakanMap[t.ID_Billing], t.Kode)
}
// Ngambil semua ICD9 yang ada
icd9Map := make(map[int][]string)
var icd9Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd9\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD9\" as \"Kode\"").
Scan(&icd9Rows).Error; err != nil {
return nil, err
}
for _, row := range icd9Rows {
icd9Map[row.ID_Billing] = append(icd9Map[row.ID_Billing], row.Kode)
}
// Ngambil semua ICD10 yang ada
icd10Map := make(map[int][]string)
var icd10Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd10\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD10\" as \"Kode\"").
Scan(&icd10Rows).Error; err != nil {
return nil, err
}
for _, row := range icd10Rows {
icd10Map[row.ID_Billing] = append(icd10Map[row.ID_Billing], row.Kode)
}
// Ngambil INACBG RI
inacbgRIMap := make(map[int][]string)
var inacbgRIRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_ri\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RI\" as \"Kode\"").
Scan(&inacbgRIRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRIRows {
inacbgRIMap[row.ID_Billing] = append(inacbgRIMap[row.ID_Billing], row.Kode)
}
// Ngambil INACBG RJ
inacbgRJMap := make(map[int][]string)
var inacbgRJRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_rj\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RJ\" as \"Kode\"").
Scan(&inacbgRJRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRJRows {
inacbgRJMap[row.ID_Billing] = append(inacbgRJMap[row.ID_Billing], row.Kode)
}
// Ambil dokter dari tabel billing_dokter, diurutkan berdasarkan tanggal
dokterMap := make(map[int][]string)
var dokterRows []struct {
ID_Billing int
Nama string
Tanggal time.Time
}
if err := db.Table("\"billing_dokter\"").
Select("\"ID_Billing\", \"Nama_Dokter\" as \"Nama\", \"tanggal\"").
Joins("JOIN \"dokter\" ON \"billing_dokter\".\"ID_Dokter\" = \"dokter\".\"ID_Dokter\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Order("\"tanggal\" ASC").
Scan(&dokterRows).Error; err != nil {
return nil, err
}
for _, row := range dokterRows {
dokterMap[row.ID_Billing] = append(dokterMap[row.ID_Billing], row.Nama)
}
// Ambil nama ruangan buat di-mapping dari ID jadi Nama
ruanganNameMap := make(map[string]string)
var ruanganRows []struct {
ID_Ruangan string
Nama_Ruangan string
}
if err := db.Table("\"ruangan\"").
Select("\"ID_Ruangan\", \"Nama_Ruangan\"").
Scan(&ruanganRows).Error; err != nil {
log.Printf("[WARNING] Gagal ngambil ruangan: %v\\n", err)
// Lanjutin aja, ID jadi fallback
} else {
for _, row := range ruanganRows {
ruanganNameMap[row.ID_Ruangan] = row.Nama_Ruangan
}
log.Printf("[DEBUG] Loaded %d ruangan mappings\n", len(ruanganNameMap))
}
// Rapihin semua data jadi response yang bagus
var result []models.Request_Admin_Inacbg
for _, b := range billings {
pasien := pasienMap[b.ID_Pasien]
// ruangan bisa jadi udah nama, bukan ID, langsung pake aja
ruanganDisplay := pasien.Ruangan
// Tapi kalo mirip ID dan ada mapping, pake nama yang sudah dimapping
if mappedName, exists := ruanganNameMap[pasien.Ruangan]; exists && mappedName != "" {
ruanganDisplay = mappedName
}
item := models.Request_Admin_Inacbg{
ID_Billing: b.ID_Billing,
Nama_pasien: pasien.Nama_Pasien,
ID_Pasien: b.ID_Pasien,
Kelas: pasien.Kelas,
Ruangan: ruanganDisplay, // ← Use name directly if available, or mapped name
Total_Tarif_RS: b.Total_Tarif_RS,
Total_Klaim: b.Total_Klaim,
Tindakan_RS: tindakanMap[b.ID_Billing],
ICD9: icd9Map[b.ID_Billing],
ICD10: icd10Map[b.ID_Billing],
INACBG_RI: inacbgRIMap[b.ID_Billing],
INACBG_RJ: inacbgRJMap[b.ID_Billing],
Billing_sign: b.Billing_sign,
Nama_Dokter: dokterMap[b.ID_Billing],
}
result = append(result, item)
}
return result, nil
}
// GetBillingByID - Get specific billing data by ID
func GetBillingByID(db *gorm.DB, id string) (map[string]interface{}, error) {
var billing models.BillingPasien
if err := db.Where("\"ID_Billing\" = ?", id).First(&billing).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, fmt.Errorf("billing dengan ID=%s tidak ditemukan", id)
}
return nil, fmt.Errorf("gagal mengambil billing: %w", err)
}
result := map[string]interface{}{
"id_billing": billing.ID_Billing,
"id_pasien": billing.ID_Pasien,
"cara_bayar": billing.Cara_Bayar,
"tanggal_masuk": billing.Tanggal_masuk,
"tanggal_keluar": billing.Tanggal_keluar,
"total_tarif_rs": billing.Total_Tarif_RS,
"total_klaim": billing.Total_Klaim,
"billing_sign": billing.Billing_sign,
}
return result, nil
}
+284
View File
@@ -0,0 +1,284 @@
package services
import (
"errors"
"fmt"
"net/smtp"
"os"
"strings"
"backendcareit/database"
"backendcareit/models"
"gorm.io/gorm"
)
// SendEmail - Ngirim email pake SMTP bro
func SendEmail(to, subject, body string) error {
// Ambil konfigurasi dari env variable dulu, lebih aman
from := os.Getenv("EMAIL_FROM")
password := os.Getenv("EMAIL_PASSWORD")
smtpHost := os.Getenv("SMTP_HOST")
smtpPort := os.Getenv("SMTP_PORT")
// Kalau env variable gak ada, pake default value (biar kompatibel sama versi lama)
if from == "" {
from = "careit565@gmail.com"
}
if password == "" {
password = "gkhz bjax uamw xydf"
}
if smtpHost == "" {
smtpHost = "smtp.gmail.com"
}
if smtpPort == "" {
smtpPort = "587"
}
if from == "" || password == "" || smtpHost == "" || smtpPort == "" {
return fmt.Errorf("konfigurasi email tidak lengkap. Pastikan EMAIL_FROM, EMAIL_PASSWORD, SMTP_HOST, dan SMTP_PORT sudah di-set")
}
// Setup authentication
auth := smtp.PlainAuth("", from, password, smtpHost)
// Format email message
msg := []byte(fmt.Sprintf("To: %s\r\n", to) +
fmt.Sprintf("Subject: %s\r\n", subject) +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" +
"\r\n" +
body + "\r\n")
// Send email
addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
err := smtp.SendMail(addr, auth, from, []string{to}, msg)
if err != nil {
return fmt.Errorf("gagal mengirim email: %w", err)
}
return nil
}
// SendEmailToMultiple - Ngirim email ke banyak orang sekaligus
func SendEmailToMultiple(to []string, subject, body string) error {
from := os.Getenv("EMAIL_FROM")
password := os.Getenv("EMAIL_PASSWORD")
smtpHost := os.Getenv("SMTP_HOST")
smtpPort := os.Getenv("SMTP_PORT")
if from == "" {
from = "asikmahdi@gmail.com"
}
if password == "" {
password = "njom rhxb prrj tuoj"
}
if smtpHost == "" {
smtpHost = "smtp.gmail.com"
}
if smtpPort == "" {
smtpPort = "587"
}
if from == "" || password == "" || smtpHost == "" || smtpPort == "" {
return fmt.Errorf("konfigurasi email tidak lengkap")
}
if len(to) == 0 {
return fmt.Errorf("daftar penerima email tidak boleh kosong")
}
// Setup authentication
auth := smtp.PlainAuth("", from, password, smtpHost)
// Rapihin header To buat semua penerima
toHeader := strings.Join(to, ", ")
// Format email message
msg := []byte(fmt.Sprintf("To: %s\r\n", toHeader) +
fmt.Sprintf("Subject: %s\r\n", subject) +
"MIME-Version: 1.0\r\n" +
"Content-Type: text/html; charset=UTF-8\r\n" +
"\r\n" +
body + "\r\n")
// Kirim email ke semua orang sekaligus
addr := fmt.Sprintf("%s:%s", smtpHost, smtpPort)
err := smtp.SendMail(addr, auth, from, to, msg)
if err != nil {
return fmt.Errorf("gagal mengirim email: %w", err)
}
return nil
}
// SendEmailTest - Cuma buat test kirim email ke teman-teman
func SendEmailTest() error {
to := []string{"stylohype685@gmail.com", "pasaribumonica2@gmail.com", "yestondehaan607@gmail.com"}
subject := "Test Email - Sistem Billing Care IT"
body := `
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
.content { background-color: #f9f9f9; padding: 20px; margin-top: 20px; }
.footer { margin-top: 20px; padding: 10px; text-align: center; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Test Email - Sistem Billing Care IT</h2>
</div>
<div class="content">
<p>Halo!</p>
<p>Ini adalah email test dari sistem billing Care IT.</p>
<p>Jika Anda menerima email ini, berarti sistem email berfungsi dengan baik.</p>
<p>Terima kasih!</p>
</div>
<div class="footer">
<p>Sistem Billing Care IT</p>
<p>Email ini dikirim untuk keperluan testing.</p>
</div>
</div>
</body>
</html>
`
if err := SendEmailToMultiple(to, subject, body); err != nil {
return fmt.Errorf("gagal mengirim email test: %w", err)
}
return nil
}
// SendEmailBillingSignToDokter mengirim email ke semua dokter yang menangani pasien tentang billing sign
func SendEmailBillingSignToDokter(idBilling int) error {
// 1. Ambil billing berdasarkan ID_Billing
var billing models.BillingPasien
if err := database.DB.First(&billing, idBilling).Error; err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return fmt.Errorf("billing dengan ID_Billing=%d tidak ditemukan", idBilling)
}
return fmt.Errorf("gagal mengambil billing: %w", err)
}
// 2. Ambil semua dokter dari billing_dokter
var dokterList []models.Dokter
if err := database.DB.
Table("\"billing_dokter\" bd").
Select("d.*").
Joins("JOIN \"dokter\" d ON bd.\"ID_Dokter\" = d.\"ID_Dokter\"").
Where("bd.\"ID_Billing\" = ?", idBilling).
Find(&dokterList).Error; err != nil {
return fmt.Errorf("gagal mengambil dokter: %w", err)
}
if len(dokterList) == 0 {
return fmt.Errorf("tidak ada dokter yang terkait dengan billing ID_Billing=%d", idBilling)
}
// 3. Ambil data pasien untuk informasi lengkap
var pasien models.Pasien
if err := database.DB.Where("\"ID_Pasien\" = ?", billing.ID_Pasien).First(&pasien).Error; err != nil {
return fmt.Errorf("gagal mengambil data pasien: %w", err)
}
// 4. Format billing sign untuk ditampilkan
billingSignDisplay := strings.ToUpper(billing.Billing_sign)
if billingSignDisplay == "" {
billingSignDisplay = "Belum ditentukan"
}
// Untuk pengiriman ke dokter: kirim personalisasi per dokter (salam pakai nama dokter)
// Kumpulkan alamat per dokter dan jalankan pengiriman secara async (goroutine)
anyEmail := false
subject := fmt.Sprintf("Notifikasi Billing Sign - Pasien: %s", pasien.Nama_Pasien)
for _, dokter := range dokterList {
// kumpulkan alamat untuk dokter ini
addrs := make([]string, 0, 2)
if e := strings.TrimSpace(dokter.Email_UB); e != "" {
addrs = append(addrs, e)
}
if e := strings.TrimSpace(dokter.Email_Pribadi); e != "" {
// hindari duplikat antara UB dan pribadi
if len(addrs) == 0 || addrs[0] != e {
addrs = append(addrs, e)
}
}
if len(addrs) == 0 {
continue
}
anyEmail = true
// buat body yang dipersonalisasi untuk dokter ini
doctorName := dokter.Nama_Dokter
if doctorName == "" {
doctorName = "Dokter"
}
bodyForDokter := fmt.Sprintf(`
<html>
<head>
<style>
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
.header { background-color: #4CAF50; color: white; padding: 20px; text-align: center; }
.content { background-color: #f9f9f9; padding: 20px; margin-top: 20px; }
.info-row { margin: 10px 0; }
.label { font-weight: bold; }
.billing-sign { font-size: 18px; font-weight: bold; padding: 10px; text-align: center; margin: 20px 0; }
.sign-hijau { background-color: #4CAF50; color: white; }
.sign-kuning { background-color: #FFC107; color: #333; }
.sign-orange { background-color: #FF9800; color: white; }
.sign-merah { background-color: #F44336; color: white; }
.footer { margin-top: 20px; padding: 10px; text-align: center; font-size: 12px; color: #666; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h2>Notifikasi Billing Sign</h2>
</div>
<div class="content">
<p>Yth. Dr. %s,</p>
<p>Berikut adalah informasi billing sign untuk pasien yang Anda tangani:</p>
<div class="info-row"><span class="label">Nama Pasien:</span> %s</div>
<div class="info-row"><span class="label">ID Billing:</span> %d</div>
<div class="info-row"><span class="label">Ruangan:</span> %s</div>
<div class="info-row"><span class="label">Kelas:</span> %s</div>
<div class="info-row"><span class="label">Cara Bayar:</span> %s</div>
<div class="info-row"><span class="label">Total Tarif RS:</span> Rp %.2f</div>
<div class="info-row"><span class="label">Total Klaim BPJS:</span> Rp %.2f</div>
<div class="billing-sign sign-%s">Billing Sign: %s</div>
<p>Terima kasih atas perhatiannya.</p>
</div>
<div class="footer"><p>Sistem Billing Care IT</p><p>Email ini dikirim secara otomatis, mohon tidak membalas email ini.</p></div>
</div>
</body>
</html>
`, doctorName, pasien.Nama_Pasien, billing.ID_Billing, pasien.Ruangan, pasien.Kelas,
billing.Cara_Bayar, billing.Total_Tarif_RS, billing.Total_Klaim,
strings.ToLower(billing.Billing_sign), billingSignDisplay)
// kirim async ke alamat dokter ini
go func(addrs []string, subj, body string, id int) {
if err := SendEmailToMultiple(addrs, subj, body); err != nil {
fmt.Printf("Warning: Gagal mengirim email ke %v untuk billing %d: %v\n", addrs, id, err)
} else {
fmt.Printf("Info: Email notifikasi terkirim ke %v untuk billing %d\n", addrs, id)
}
}(addrs, subject, bodyForDokter, billing.ID_Billing)
}
if !anyEmail {
return fmt.Errorf("tidak ada dokter dengan email yang terdaftar untuk billing ID_Billing=%d", idBilling)
}
// Return immediately; actual sending berjalan di goroutine
return nil
}
+169
View File
@@ -0,0 +1,169 @@
package services
import (
"backendcareit/models"
"gorm.io/gorm"
)
func GetAllBillingaktif(db *gorm.DB) ([]models.Request_Admin_Inacbg, error) {
var billings []models.BillingPasien
// Ambil semua billing yang masih aktif (belum ditutup, Tanggal_Keluar masih kosong)
if err := db.Where("\"Tanggal_Keluar\" IS NULL").Find(&billings).Error; err != nil {
return nil, err
}
// Kumpulin dulu semua ID_Billing dan ID_Pasien buat di-query
var billingIDs []int
var pasienIDs []int
for _, b := range billings {
billingIDs = append(billingIDs, b.ID_Billing)
pasienIDs = append(pasienIDs, b.ID_Pasien)
}
// Ambil pasien yang ada di billing aja
pasienMap := make(map[int]models.Pasien)
var pasienList []models.Pasien
if err := db.Where("\"ID_Pasien\" IN ?", pasienIDs).Find(&pasienList).Error; err != nil {
return nil, err
}
for _, p := range pasienList {
pasienMap[p.ID_Pasien] = p
}
// Ambil tindakan yang berkaitan sama billing-billing ini
tindakanMap := make(map[int][]string)
var tindakanRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanRows {
tindakanMap[t.ID_Billing] = append(tindakanMap[t.ID_Billing], t.Kode)
}
// Ambil ICD9
icd9Map := make(map[int][]string)
var icd9Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd9\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD9\" as \"Kode\"").
Scan(&icd9Rows).Error; err != nil {
return nil, err
}
for _, row := range icd9Rows {
icd9Map[row.ID_Billing] = append(icd9Map[row.ID_Billing], row.Kode)
}
// Ambil ICD10
icd10Map := make(map[int][]string)
var icd10Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd10\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD10\" as \"Kode\"").
Scan(&icd10Rows).Error; err != nil {
return nil, err
}
for _, row := range icd10Rows {
icd10Map[row.ID_Billing] = append(icd10Map[row.ID_Billing], row.Kode)
}
// Ambil INACBG RI
inacbgRIMap := make(map[int][]string)
var inacbgRIRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_ri\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RI\" as \"Kode\"").
Scan(&inacbgRIRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRIRows {
inacbgRIMap[row.ID_Billing] = append(inacbgRIMap[row.ID_Billing], row.Kode)
}
// Ambil INACBG RJ
inacbgRJMap := make(map[int][]string)
var inacbgRJRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_rj\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RJ\" as \"Kode\"").
Scan(&inacbgRJRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRJRows {
inacbgRJMap[row.ID_Billing] = append(inacbgRJMap[row.ID_Billing], row.Kode)
}
// Ambil dokter dari billing_dokter dengan urutan tanggal
dokterMap := make(map[int][]string)
var dokterRows []struct {
ID_Billing int
Nama string
}
if err := db.Table("\"billing_dokter\"").
Select("\"ID_Billing\", \"Nama_Dokter\" as \"Nama\"").
Joins("JOIN \"dokter\" ON \"billing_dokter\".\"ID_Dokter\" = \"dokter\".\"ID_Dokter\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Order("tanggal ASC").
Scan(&dokterRows).Error; err != nil {
return nil, err
}
for _, row := range dokterRows {
dokterMap[row.ID_Billing] = append(dokterMap[row.ID_Billing], row.Nama)
}
// Rapihin semua data jadi response yang keren
var result []models.Request_Admin_Inacbg
for _, b := range billings {
pasien := pasienMap[b.ID_Pasien]
item := models.Request_Admin_Inacbg{
ID_Billing: b.ID_Billing,
Nama_pasien: pasien.Nama_Pasien,
ID_Pasien: b.ID_Pasien,
Kelas: pasien.Kelas,
Ruangan: pasien.Ruangan,
Total_Tarif_RS: b.Total_Tarif_RS,
Total_Klaim: b.Total_Klaim,
Tindakan_RS: tindakanMap[b.ID_Billing],
ICD9: icd9Map[b.ID_Billing],
ICD10: icd10Map[b.ID_Billing],
INACBG_RI: inacbgRIMap[b.ID_Billing],
INACBG_RJ: inacbgRJMap[b.ID_Billing],
Billing_sign: b.Billing_sign,
Nama_Dokter: dokterMap[b.ID_Billing],
}
result = append(result, item)
}
return result, nil
}
+673
View File
@@ -0,0 +1,673 @@
package services
import (
"errors"
"fmt"
"log"
"strings"
"time"
"backendcareit/database"
"backendcareit/models"
"gorm.io/gorm"
)
// Ambil ID_tarif_RS dari nama Tindakan_RS
func GetTarifRSByTindakan(tindakans []string) ([]models.TarifRS, error) {
var tarifList []models.TarifRS
if err := database.DB.
Where("\"Tindakan_RS\" IN ?", tindakans).
Find(&tarifList).Error; err != nil {
return nil, err
}
return tarifList, nil
}
// GetPasienByID - Cari pasien berdasarkan ID nya
func GetPasienByID(id int) (*models.Pasien, error) {
var pasien models.Pasien
if err := database.DB.Where("\"ID_Pasien\" = ?", id).First(&pasien).Error; err != nil {
return nil, err
}
return &pasien, nil
}
// GetPasienByNama - Cari pasien berdasarkan nama mereka
func GetPasienByNama(nama string) (*models.Pasien, error) {
var pasien models.Pasien
if err := database.DB.Where("\"Nama_Pasien\" = ?", nama).First(&pasien).Error; err != nil {
return nil, err
}
return &pasien, nil
}
// SearchPasienByNama - Pencarian pasien pake nama (bisa partial)
func SearchPasienByNama(nama string) ([]models.Pasien, error) {
var pasien []models.Pasien
err := database.DB.
Where("\"Nama_Pasien\" LIKE ?", "%"+nama+"%").
Find(&pasien).Error
if err != nil {
return nil, err
}
return pasien, nil
}
// GetBillingDetailAktifByNama - Ambil data billing lengkap (billing, tindakan, ICD, dokter, INACBG, DPJP) buat satu pasien dari nama
// Return: billing, tindakan, icd9, icd10, dokter, inacbgRI, inacbgRJ, dpjp, error
func GetBillingDetailAktifByNama(namaPasien string) (*models.BillingPasien, []string, []string, []string, []string, []string, []string, int, error) {
// Cari pasien dulu
var pasien models.Pasien
if err := database.DB.Where("\"Nama_Pasien\" = ?", namaPasien).First(&pasien).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
// Cari billing aktif terakhir pasien ini (yang belum ditutup, Tanggal_Keluar IS NULL)
var billing models.BillingPasien
if err := database.DB.
Where("\"ID_Pasien\" = ? AND \"Tanggal_Keluar\" IS NULL", pasien.ID_Pasien).
Order("\"ID_Billing\" DESC").
First(&billing).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
// Ambil semua tindakan (join billing_tindakan -> tarif_rs)
var tindakanJoin []struct {
Nama string `gorm:"column:Tindakan_RS"`
}
if err := database.DB.
Table("\"billing_tindakan\" bt").
Select("tr.\"Tindakan_RS\"").
Joins("JOIN \"tarif_rs\" tr ON bt.\"ID_Tarif_RS\" = tr.\"ID_Tarif_RS\"").
Where("bt.\"ID_Billing\" = ?", billing.ID_Billing).
Scan(&tindakanJoin).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
tindakanNames := make([]string, 0, len(tindakanJoin))
for _, t := range tindakanJoin {
tindakanNames = append(tindakanNames, t.Nama)
}
// Ambil semua ICD9
var icd9Join []struct {
Prosedur string `gorm:"column:Prosedur"`
}
if err := database.DB.
Table("\"billing_icd9\" bi").
Select("i.\"Prosedur\"").
Joins("JOIN \"icd9\" i ON bi.\"ID_ICD9\" = i.\"ID_ICD9\"").
Where("bi.\"ID_Billing\" = ?", billing.ID_Billing).
Scan(&icd9Join).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
icd9Names := make([]string, 0, len(icd9Join))
for _, i := range icd9Join {
icd9Names = append(icd9Names, i.Prosedur)
}
// Ambil semua ICD10
var icd10Join []struct {
Diagnosa string `gorm:"column:Diagnosa"`
}
if err := database.DB.
Table("\"billing_icd10\" bi").
Select("i.\"Diagnosa\"").
Joins("JOIN \"icd10\" i ON bi.\"ID_ICD10\" = i.\"ID_ICD10\"").
Where("bi.\"ID_Billing\" = ?", billing.ID_Billing).
Scan(&icd10Join).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
icd10Names := make([]string, 0, len(icd10Join))
for _, i := range icd10Join {
icd10Names = append(icd10Names, i.Diagnosa)
}
// Ambil semua dokter dari billing_dokter dengan tanggal
var dokterJoin []struct {
Nama string `gorm:"column:Nama_Dokter"`
Tanggal *time.Time `gorm:"column:Tanggal"`
}
if err := database.DB.
Table("\"billing_dokter\"").
Select("\"Nama_Dokter\", \"tanggal\"").
Joins("JOIN \"dokter\" ON \"billing_dokter\".\"ID_Dokter\" = \"dokter\".\"ID_Dokter\"").
Where("\"billing_dokter\".\"ID_Billing\" = ?", billing.ID_Billing).
Order("tanggal ASC").
Scan(&dokterJoin).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
dokterNames := make([]string, 0, len(dokterJoin))
for _, d := range dokterJoin {
dokterNames = append(dokterNames, d.Nama)
}
// Ambil semua INACBG RI
var inacbgRIJoin []struct {
Kode string `gorm:"column:ID_INACBG_RI"`
}
if err := database.DB.
Table("\"billing_inacbg_ri\"").
Select("\"ID_INACBG_RI\"").
Where("\"ID_Billing\" = ?", billing.ID_Billing).
Scan(&inacbgRIJoin).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
inacbgRINames := make([]string, 0, len(inacbgRIJoin))
for _, row := range inacbgRIJoin {
inacbgRINames = append(inacbgRINames, row.Kode)
}
// Ambil semua INACBG RJ
var inacbgRJJoin []struct {
Kode string `gorm:"column:ID_INACBG_RJ"`
}
if err := database.DB.
Table("\"billing_inacbg_rj\"").
Select("\"ID_INACBG_RJ\"").
Where("\"ID_Billing\" = ?", billing.ID_Billing).
Scan(&inacbgRJJoin).Error; err != nil {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
inacbgRJNames := make([]string, 0, len(inacbgRJJoin))
for _, row := range inacbgRJJoin {
inacbgRJNames = append(inacbgRJNames, row.Kode)
}
// Ambil DPJP (Doctor In Charge) dari billing_dpjp
var dpjpRow struct {
ID_DPJP int `gorm:"column:ID_DPJP"`
}
var idDPJP int
if err := database.DB.
Table("\"billing_dpjp\"").
Select("\"ID_DPJP\"").
Where("\"ID_Billing\" = ?", billing.ID_Billing).
First(&dpjpRow).Error; err != nil {
if !errors.Is(err, gorm.ErrRecordNotFound) {
return nil, nil, nil, nil, nil, nil, nil, 0, err
}
// Jika tidak ada DPJP, idDPJP = 0 (normal, boleh tidak ada)
idDPJP = 0
} else {
idDPJP = dpjpRow.ID_DPJP
}
return &billing, tindakanNames, icd9Names, icd10Names, dokterNames, inacbgRINames, inacbgRJNames, idDPJP, nil
}
// GetDokterByNama - Cari dokter berdasarkan nama mereka
func GetDokterByNama(nama string) (*models.Dokter, error) {
var dokter models.Dokter
if err := database.DB.Where("\"Nama_Dokter\" = ?", nama).First(&dokter).Error; err != nil {
return nil, err
}
return &dokter, nil
}
func DataFromFE(input models.BillingRequest) (
*models.BillingPasien,
*models.Pasien,
[]models.Billing_Tindakan,
[]models.Billing_ICD9,
[]models.Billing_ICD10,
error,
) {
tx := database.DB.Begin()
if tx.Error != nil {
return nil, nil, nil, nil, nil, tx.Error
}
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// ===========================
// 1. CARI ATAU BUAT PASIEN
// ===========================
var pasien models.Pasien
result := tx.Where("\"Nama_Pasien\" = ?", input.Nama_Pasien).First(&pasien)
// Jika pasien sudah ada, update data jika ada perubahan (usia, ruangan, kelas, jenis_kelamin)
if result.Error == nil {
updated := false
if pasien.Usia != input.Usia {
pasien.Usia = input.Usia
updated = true
}
if pasien.Ruangan != input.Ruangan {
pasien.Ruangan = input.Ruangan
updated = true
}
if pasien.Kelas != input.Kelas {
pasien.Kelas = input.Kelas
updated = true
}
if pasien.Jenis_Kelamin != input.Jenis_Kelamin {
pasien.Jenis_Kelamin = input.Jenis_Kelamin
updated = true
}
if updated {
if err := tx.Save(&pasien).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal update data pasien: %s", err.Error())
}
}
}
if result.Error != nil {
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
pasien = models.Pasien{
Nama_Pasien: input.Nama_Pasien,
Jenis_Kelamin: input.Jenis_Kelamin,
Usia: input.Usia,
Ruangan: input.Ruangan,
Kelas: input.Kelas,
}
if err := tx.Create(&pasien).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal membuat pasien baru: %s", err.Error())
}
} else {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal mencari pasien: %s", result.Error.Error())
}
}
if pasien.ID_Pasien == 0 {
tx.Rollback()
return nil, nil, nil, nil, nil, fmt.Errorf("ID_Pasien tidak valid")
}
// ===========================
// 2. CARI SEMUA DOKTER
// ===========================
var dokterList []models.Dokter
for _, namaDokter := range input.Nama_Dokter {
var dokter models.Dokter
if err := tx.Where("\"Nama_Dokter\" = ?", namaDokter).First(&dokter).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("dokter '%s' tidak ditemukan", namaDokter)
}
dokterList = append(dokterList, dokter)
}
now := time.Now()
// Parse Tanggal_Keluar (frontend sends string). Accept multiple formats.
var keluarPtr *time.Time
if input.Tanggal_Keluar != "" && input.Tanggal_Keluar != "null" {
s := input.Tanggal_Keluar
// Try several common layouts
var parsed time.Time
var err error
layouts := []string{time.RFC3339, "2006-01-02 15:04:05", "2006-01-02"}
for _, layout := range layouts {
parsed, err = time.Parse(layout, s)
if err == nil {
t := parsed
keluarPtr = &t
break
}
}
if keluarPtr == nil {
// If parsing failed, return error
tx.Rollback()
return nil, nil, nil, nil, nil, fmt.Errorf("invalid tanggal_keluar format: %s", input.Tanggal_Keluar)
}
}
// ===========================
// 3. CARI / BUAT BILLING
// ===========================
// Catatan:
// - Kita anggap "billing aktif" = billing yang belum ditutup (Tanggal_Keluar IS NULL) untuk pasien ini.
// - Jika ada billing aktif, update; jika tidak, buat billing baru.
var billing models.BillingPasien
billingResult := tx.
Where("\"ID_Pasien\" = ? AND \"Tanggal_Keluar\" IS NULL", pasien.ID_Pasien).
Order("\"ID_Billing\" DESC").
First(&billing)
if billingResult.Error != nil {
if errors.Is(billingResult.Error, gorm.ErrRecordNotFound) {
// Belum ada billing aktif → buat billing baru
billing = models.BillingPasien{
ID_Pasien: pasien.ID_Pasien,
Cara_Bayar: input.Cara_Bayar,
Tanggal_masuk: &now,
Tanggal_keluar: keluarPtr,
Total_Tarif_RS: input.Total_Tarif_RS,
Total_Klaim: input.Total_Klaim_BPJS, // ← Changed: Use input value instead of hardcoded 0
}
// jika frontend mengirim billing_sign, gunakan itu, kalau tidak gunakan default ""
if input.Billing_sign != "" {
billing.Billing_sign = input.Billing_sign
} else {
billing.Billing_sign = ""
}
if err := tx.Create(&billing).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal membuat billing: %s", err.Error())
}
} else {
// Error lain saat cari billing
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal mencari billing pasien: %s", billingResult.Error.Error())
}
} else {
// Sudah ada billing aktif → update data billing lama, tambahkan tindakan / ICD baru
billing.Cara_Bayar = input.Cara_Bayar
if keluarPtr != nil {
billing.Tanggal_keluar = keluarPtr
}
// Tambahkan total tarif dari request baru
billing.Total_Tarif_RS += input.Total_Tarif_RS
// Update Total_Tarif_BPJS if:
// 1. Not yet set (== 0), OR
// 2. Input value is higher (more accurate baseline from FE)
// This ensures we always have the correct baseline, not accumulated value from INACBG
if input.Total_Klaim_BPJS > 0 && (billing.Total_Klaim == 0 || input.Total_Klaim_BPJS > billing.Total_Klaim) {
billing.Total_Klaim = input.Total_Klaim_BPJS
log.Printf("[Billing] Updated Total_Tarif_BPJS to %.2f\n", input.Total_Klaim_BPJS)
}
// Log input billing_sign untuk debug
log.Printf("[Billing Update] Received input.Billing_sign: '%s' (empty=%v)\n", input.Billing_sign, input.Billing_sign == "")
// Jika frontend mengirim Billing_sign, gunakan; jika tidak, hitung di backend
if input.Billing_sign != "" {
billing.Billing_sign = input.Billing_sign
log.Printf("[Billing Update] Updated Billing_sign to: %s\n", input.Billing_sign)
}
if err := tx.Save(&billing).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal update billing pasien: %s", err.Error())
}
// Jika frontend mengirim Billing_sign pada update, kirim notifikasi email ke dokter secara async
}
// ===========================
// 4. SIMPAN DOKTER KE BILLING_DOKTER DENGAN TANGGAL
// ===========================
// Tidak menghapus dokter lama, hanya menambahkan dokter baru dengan tanggal hari ini
// Ini memungkinkan tracking dokter yang berbeda setiap hari
tanggalHariIni := time.Now()
// Insert semua dokter baru ke billing_dokter dengan tanggal hari ini
// Cek dulu apakah dokter dengan tanggal yang sama sudah ada (untuk menghindari duplikasi)
var billingDokterList []models.Billing_Dokter
for _, dokter := range dokterList {
// Cek apakah dokter ini sudah ada di billing dengan tanggal yang sama
var existing models.Billing_Dokter
result := tx.Where("\"ID_Billing\" = ? AND \"ID_Dokter\" = ? AND DATE(tanggal) = DATE(?)",
billing.ID_Billing, dokter.ID_Dokter, tanggalHariIni).First(&existing)
// Jika belum ada, tambahkan
if result.Error != nil && errors.Is(result.Error, gorm.ErrRecordNotFound) {
billingDokter := models.Billing_Dokter{
ID_Billing: billing.ID_Billing,
ID_Dokter: dokter.ID_Dokter,
Tanggal: &tanggalHariIni,
}
billingDokterList = append(billingDokterList, billingDokter)
}
// Jika sudah ada, skip (tidak perlu insert lagi)
}
if len(billingDokterList) > 0 {
if err := tx.Create(&billingDokterList).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal insert billing dokter: %s", err.Error())
}
}
// ===========================
// 4.5 SIMPAN DPJP KE BILLING_DPJP
// ===========================
// Insert DPJP (Doctor In Charge) ke tabel billing_dpjp jika ID_DPJP disediakan
if input.ID_DPJP > 0 {
billingDPJP := models.Billing_DPJP{
ID_Billing: billing.ID_Billing,
ID_DPJP: input.ID_DPJP,
}
if err := tx.Create(&billingDPJP).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal insert billing DPJP: %s", err.Error())
}
log.Printf("[Billing DPJP] Inserted billing %d with DPJP %d\n", billing.ID_Billing, input.ID_DPJP)
}
var billingTindakanList []models.Billing_Tindakan
var billingICD9List []models.Billing_ICD9
var billingICD10List []models.Billing_ICD10
for _, tindakan := range input.Tindakan_RS {
var tarif models.TarifRS
if err := tx.Where("\"Tindakan_RS\" = ?", tindakan).First(&tarif).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("tindakan '%s' tidak ditemukan", tindakan)
}
billTindakan := models.Billing_Tindakan{
ID_Billing: billing.ID_Billing,
ID_Tarif_RS: tarif.KodeRS,
Tanggal_Tindakan: &tanggalHariIni,
}
if err := tx.Create(&billTindakan).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal insert billing tindakan: %s", err.Error())
}
billingTindakanList = append(billingTindakanList, billTindakan)
}
for _, icd := range input.ICD9 {
var icd9 models.ICD9
if err := tx.Where("\"Prosedur\" = ?", icd).First(&icd9).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("ICD9 '%s' tidak ditemukan", icd)
}
billICD9 := models.Billing_ICD9{
ID_Billing: billing.ID_Billing,
ID_ICD9: icd9.Kode_ICD9,
}
if err := tx.Create(&billICD9).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal insert billing ICD9: %s", err.Error())
}
billingICD9List = append(billingICD9List, billICD9)
}
for _, icd := range input.ICD10 {
var icd10 models.ICD10
if err := tx.Where("\"Diagnosa\" = ?", icd).First(&icd10).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("ICD10 '%s' tidak ditemukan", icd)
}
billICD10 := models.Billing_ICD10{
ID_Billing: billing.ID_Billing,
ID_ICD10: icd10.Kode_ICD10,
}
if err := tx.Create(&billICD10).Error; err != nil {
tx.Rollback()
return nil, nil, nil, nil, nil,
fmt.Errorf("gagal insert billing ICD10: %s", err.Error())
}
billingICD10List = append(billingICD10List, billICD10)
}
if err := tx.Commit().Error; err != nil {
return nil, nil, nil, nil, nil, err
}
if input.Billing_sign != "" && strings.TrimSpace(input.Billing_sign) != "" {
go func(id int) {
if err := SendEmailBillingSignToDokter(id); err != nil {
fmt.Printf("Warning: Gagal mengirim email ke dokter untuk billing ID %d: %v\n", id, err)
}
}(billing.ID_Billing)
}
return &billing, &pasien, billingTindakanList, billingICD9List, billingICD10List, nil
}
// GetLastBillingByNama - Ambil billing terakhir pasien (buat dapetin baseline total_klaim pas billing baru dibuat)
func GetLastBillingByNama(namaPasien string) (*models.BillingPasien, error) {
// Cari pasien dulu
var pasien models.Pasien
if err := database.DB.Where("\"Nama_Pasien\" = ?", namaPasien).First(&pasien).Error; err != nil {
return nil, err
}
// Cari billing terakhir pasien ini (paling baru berdasarkan ID_Billing)
var billing models.BillingPasien
if err := database.DB.
Where("\"ID_Pasien\" = ?", pasien.ID_Pasien).
Order("\"ID_Billing\" DESC").
First(&billing).Error; err != nil {
return nil, err
}
return &billing, nil
}
// UpdateBillingIdentitas - update data identitas pasien dalam billing
func UpdateBillingIdentitas(billingId int, namaPasien string, usia int, jeniKelamin string, ruangan string, kelas string, tindakan []string, icd9 []string, icd10 []string) error {
// Get billing
var billing models.BillingPasien
if err := database.DB.Where("\"ID_Billing\" = ?", billingId).First(&billing).Error; err != nil {
return errors.New("billing tidak ditemukan")
}
// Start transaction
tx := database.DB.Begin()
// Update pasien data
pasien := models.Pasien{}
if err := tx.Model(&pasien).
Where("\"ID_Pasien\" = ?", billing.ID_Pasien).
Updates(map[string]interface{}{
"\"Nama_Pasien\"": namaPasien,
"\"Usia\"": usia,
"\"Jenis_Kelamin\"": jeniKelamin,
"\"Ruangan\"": ruangan,
"\"Kelas\"": kelas,
}).Error; err != nil {
tx.Rollback()
return errors.New("gagal update data pasien: " + err.Error())
}
// Delete existing tindakan
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_Tindakan{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete tindakan: " + err.Error())
}
// Insert new tindakan
for _, t := range tindakan {
if t != "" {
newTindakan := models.Billing_Tindakan{
ID_Billing: billingId,
ID_Tarif_RS: t,
}
if err := tx.Create(&newTindakan).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert tindakan: " + err.Error())
}
}
}
// Delete existing ICD9
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_ICD9{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete ICD9: " + err.Error())
}
// Insert new ICD9
for _, i := range icd9 {
if i != "" {
newICD9 := models.Billing_ICD9{
ID_Billing: billingId,
ID_ICD9: i,
}
if err := tx.Create(&newICD9).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert ICD9: " + err.Error())
}
}
}
// Delete existing ICD10
if err := tx.Where("\"ID_Billing\" = ?", billingId).Delete(&models.Billing_ICD10{}).Error; err != nil {
tx.Rollback()
return errors.New("gagal delete ICD10: " + err.Error())
}
// Insert new ICD10
for _, i := range icd10 {
if i != "" {
newICD10 := models.Billing_ICD10{
ID_Billing: billingId,
ID_ICD10: i,
}
if err := tx.Create(&newICD10).Error; err != nil {
tx.Rollback()
return errors.New("gagal insert ICD10: " + err.Error())
}
}
}
// Commit transaction
if err := tx.Commit().Error; err != nil {
return errors.New("gagal commit transaction: " + err.Error())
}
return nil
}
@@ -0,0 +1,52 @@
package services
import (
"errors"
"fmt"
"time"
"backendcareit/database"
"backendcareit/models"
)
// CloseBilling - Nutup billing dengan set Tanggal_Keluar (selesai dah pasiennya)
func CloseBilling(closeReq models.Close_billing) error {
// Cari billing berdasarkan ID_Billing
var billing models.BillingPasien
if err := database.DB.Where("\"ID_Billing\" = ?", closeReq.ID_Billing).First(&billing).Error; err != nil {
return fmt.Errorf("billing dengan ID %d tidak ditemukan: %w", closeReq.ID_Billing, err)
}
// Parse Tanggal_Keluar dari string ke time.Time
// Menggunakan multiple layouts seperti di billing_pasien.go
var keluarTime *time.Time
if closeReq.Tanggal_Keluar != "" {
s := closeReq.Tanggal_Keluar
var parsed time.Time
var err error
layouts := []string{time.RFC3339, "2006-01-02 15:04:05", "2006-01-02"}
for _, layout := range layouts {
parsed, err = time.Parse(layout, s)
if err == nil {
t := parsed
keluarTime = &t
break
}
}
if keluarTime == nil {
return fmt.Errorf("format tanggal_keluar tidak valid: %s", closeReq.Tanggal_Keluar)
}
} else {
return errors.New("tanggal_keluar tidak boleh kosong")
}
// Update Tanggal_keluar pada billing
billing.Tanggal_keluar = keluarTime
// Simpan perubahan
if err := database.DB.Save(&billing).Error; err != nil {
return fmt.Errorf("gagal update billing: %w", err)
}
return nil
}
@@ -0,0 +1,403 @@
package services
import (
"backendcareit/models"
"time"
"gorm.io/gorm"
)
func GetRiwayatPasienAll(db *gorm.DB) ([]models.Riwayat_Pasien_all, error) {
var billings []models.BillingPasien
// Ngambil semua billing yang udah ditutup (Tanggal_Keluar udah ada)
if err := db.Where("\"Tanggal_Keluar\" IS NOT NULL").Find(&billings).Error; err != nil {
return nil, err
}
// Kumpulkan semua ID_Billing dan ID_Pasien
var billingIDs []int
var pasienIDs []int
for _, b := range billings {
billingIDs = append(billingIDs, b.ID_Billing)
pasienIDs = append(pasienIDs, b.ID_Pasien)
}
// Ambil pasien yang ada di billing aja
pasienMap := make(map[int]models.Pasien)
var pasienList []models.Pasien
if err := db.Where("\"ID_Pasien\" IN ?", pasienIDs).Find(&pasienList).Error; err != nil {
return nil, err
}
for _, p := range pasienList {
pasienMap[p.ID_Pasien] = p
}
// Ambil tindakan hanya untuk billing terkait
tindakanMap := make(map[int][]string)
var tindakanRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanRows {
tindakanMap[t.ID_Billing] = append(tindakanMap[t.ID_Billing], t.Kode)
}
// Ambil tanggal tindakan dari tabel billing_tindakan
tindakanDateMap := make(map[int]*time.Time)
var tindakanDateRows []struct {
ID_Billing int
Tanggal_Tindakan *time.Time
}
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"tanggal_tindakan\"").
Scan(&tindakanDateRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanDateRows {
if t.Tanggal_Tindakan != nil {
tindakanDateMap[t.ID_Billing] = t.Tanggal_Tindakan
}
}
// Ambil ICD9
icd9Map := make(map[int][]string)
var icd9Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd9\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD9\" as \"Kode\"").
Scan(&icd9Rows).Error; err != nil {
return nil, err
}
for _, row := range icd9Rows {
icd9Map[row.ID_Billing] = append(icd9Map[row.ID_Billing], row.Kode)
}
// Ambil ICD10
icd10Map := make(map[int][]string)
var icd10Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd10\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD10\" as \"Kode\"").
Scan(&icd10Rows).Error; err != nil {
return nil, err
}
for _, row := range icd10Rows {
icd10Map[row.ID_Billing] = append(icd10Map[row.ID_Billing], row.Kode)
}
// Ambil INACBG - yang RI dikasih prioritas duluan
inacbgMap := make(map[int]string)
var inacbgRIRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_ri\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RI\" as \"Kode\"").
Scan(&inacbgRIRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRIRows {
inacbgMap[row.ID_Billing] = row.Kode
}
// Kalo gada RI, ambil dari RJ aja
var inacbgRJRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_rj\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RJ\" as \"Kode\"").
Scan(&inacbgRJRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRJRows {
if _, exists := inacbgMap[row.ID_Billing]; !exists {
inacbgMap[row.ID_Billing] = row.Kode
}
}
// Ambil DPJP (Doctor In Charge) dari billing_dpjp
dpjpMap := make(map[int]int)
var dpjpRows []struct {
ID_Billing int
ID_DPJP int
}
if err := db.Table("\"billing_dpjp\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_DPJP\"").
Scan(&dpjpRows).Error; err != nil {
return nil, err
}
for _, row := range dpjpRows {
dpjpMap[row.ID_Billing] = row.ID_DPJP
}
// nama dokter susai dpjp ya gais
dpjpNameMap := make(map[int]string)
var dpjpNameRows []struct {
ID_Dokter int
Nama_Dokter string
}
if err := db.Table("\"dokter\"").
Select("\"ID_Dokter\", \"Nama_Dokter\"").
Scan(&dpjpNameRows).Error; err != nil {
return nil, err
}
for _, row := range dpjpNameRows {
dpjpNameMap[row.ID_Dokter] = row.Nama_Dokter
}
// Ambil nama ruangan buat di-mapping dari ID jadi Nama
ruanganNameMap := make(map[string]string)
var ruanganRows []struct {
ID_Ruangan string
Nama_Ruangan string
}
if err := db.Table("\"ruangan\"").
Select("\"ID_Ruangan\", \"Nama_Ruangan\"").
Scan(&ruanganRows).Error; err != nil {
return nil, err
}
for _, row := range ruanganRows {
ruanganNameMap[row.ID_Ruangan] = row.Nama_Ruangan
}
// Rapihin semua data jadi response yang bagus
var result []models.Riwayat_Pasien_all
for _, b := range billings {
pasien := pasienMap[b.ID_Pasien]
item := models.Riwayat_Pasien_all{
ID_Billing: b.ID_Billing,
ID_Pasien: pasien.ID_Pasien,
Nama_Pasien: pasien.Nama_Pasien,
Jenis_Kelamin: pasien.Jenis_Kelamin,
Usia: pasien.Usia,
Ruangan: pasien.Ruangan,
Nama_Ruangan: ruanganNameMap[pasien.Ruangan],
Kelas: pasien.Kelas,
ID_DPJP: dpjpMap[b.ID_Billing],
Nama_DPJP: dpjpNameMap[dpjpMap[b.ID_Billing]],
Tanggal_Keluar: b.Tanggal_keluar.Format("2006-01-02"),
Tanggal_Masuk: b.Tanggal_masuk.Format("2006-01-02"), //b.Tanggal_masuk,
Tanggal_Tindakan: tindakanDateMap[b.ID_Billing],
Tindakan_RS: tindakanMap[b.ID_Billing],
ICD9: icd9Map[b.ID_Billing],
ICD10: icd10Map[b.ID_Billing],
Kode_INACBG: inacbgMap[b.ID_Billing],
Total_Tarif_RS: b.Total_Tarif_RS,
Total_Klaim: b.Total_Klaim,
}
result = append(result, item)
}
return result, nil
}
func GetAllRiwayatpasien(db *gorm.DB) ([]models.Request_Admin_Inacbg, error) {
var billings []models.BillingPasien
// Ngambil semua billing yang udah ditutup (Tanggal_Keluar ada isinya)
if err := db.Where("\"Tanggal_Keluar\" IS NOT NULL").Find(&billings).Error; err != nil {
return nil, err
}
// Kumpulkan semua ID_Billing dan ID_Pasien
var billingIDs []int
var pasienIDs []int
for _, b := range billings {
billingIDs = append(billingIDs, b.ID_Billing)
pasienIDs = append(pasienIDs, b.ID_Pasien)
}
// Ambil pasien yang ada di billing aja
pasienMap := make(map[int]models.Pasien)
var pasienList []models.Pasien
if err := db.Where("\"ID_Pasien\" IN ? ", pasienIDs).Find(&pasienList).Error; err != nil {
return nil, err
}
for _, p := range pasienList {
pasienMap[p.ID_Pasien] = p
}
// Ambil tindakan hanya untuk billing terkait
tindakanMap := make(map[int][]string)
var tindakanRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_tindakan\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_Tarif_RS\" as \"Kode\"").
Scan(&tindakanRows).Error; err != nil {
return nil, err
}
for _, t := range tindakanRows {
tindakanMap[t.ID_Billing] = append(tindakanMap[t.ID_Billing], t.Kode)
}
// Ambil ICD9
icd9Map := make(map[int][]string)
var icd9Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd9\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD9\" as \"Kode\"").
Scan(&icd9Rows).Error; err != nil {
return nil, err
}
for _, row := range icd9Rows {
icd9Map[row.ID_Billing] = append(icd9Map[row.ID_Billing], row.Kode)
}
// Ambil ICD10
icd10Map := make(map[int][]string)
var icd10Rows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_icd10\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_ICD10\" as \"Kode\"").
Scan(&icd10Rows).Error; err != nil {
return nil, err
}
for _, row := range icd10Rows {
icd10Map[row.ID_Billing] = append(icd10Map[row.ID_Billing], row.Kode)
}
// Ngambil INACBG RI
inacbgRIMap := make(map[int][]string)
var inacbgRIRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_ri\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RI\" as \"Kode\"").
Scan(&inacbgRIRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRIRows {
inacbgRIMap[row.ID_Billing] = append(inacbgRIMap[row.ID_Billing], row.Kode)
}
// Ngambil INACBG RJ
inacbgRJMap := make(map[int][]string)
var inacbgRJRows []struct {
ID_Billing int
Kode string
}
if err := db.Table("\"billing_inacbg_rj\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_INACBG_RJ\" as \"Kode\"").
Scan(&inacbgRJRows).Error; err != nil {
return nil, err
}
for _, row := range inacbgRJRows {
inacbgRJMap[row.ID_Billing] = append(inacbgRJMap[row.ID_Billing], row.Kode)
}
// Ambil dokter dari tabel billing_dokter, diurutkan berdasarkan tanggal
dokterMap := make(map[int][]string)
var dokterRows []struct {
ID_Billing int
Nama string
}
if err := db.Table("\"billing_dokter\"").
Select("\"ID_Billing\", \"Nama_Dokter\" as \"Nama\"").
Joins("JOIN \"dokter\" ON \"billing_dokter\".\"ID_Dokter\" = \"dokter\".\"ID_Dokter\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Order("tanggal ASC").
Scan(&dokterRows).Error; err != nil {
return nil, err
}
for _, row := range dokterRows {
dokterMap[row.ID_Billing] = append(dokterMap[row.ID_Billing], row.Nama)
}
// Ambil DPJP (Doctor In Charge) dari billing_dpjp
dpjpMap := make(map[int]int)
var dpjpRows []struct {
ID_Billing int
ID_DPJP int
}
if err := db.Table("\"billing_dpjp\"").
Where("\"ID_Billing\" IN ?", billingIDs).
Select("\"ID_Billing\", \"ID_DPJP\"").
Scan(&dpjpRows).Error; err != nil {
return nil, err
}
for _, row := range dpjpRows {
dpjpMap[row.ID_Billing] = row.ID_DPJP
}
// Rapihin semua data jadi response yang bagus
var result []models.Request_Admin_Inacbg
for _, b := range billings {
pasien := pasienMap[b.ID_Pasien]
item := models.Request_Admin_Inacbg{
ID_Billing: b.ID_Billing,
Nama_pasien: pasien.Nama_Pasien,
ID_Pasien: pasien.ID_Pasien,
Kelas: pasien.Kelas,
Ruangan: pasien.Ruangan,
Total_Tarif_RS: b.Total_Tarif_RS,
Total_Klaim: b.Total_Klaim,
ID_DPJP: dpjpMap[b.ID_Billing],
Tindakan_RS: tindakanMap[b.ID_Billing],
ICD9: icd9Map[b.ID_Billing],
ICD10: icd10Map[b.ID_Billing],
INACBG_RI: inacbgRIMap[b.ID_Billing],
INACBG_RJ: inacbgRJMap[b.ID_Billing],
Billing_sign: b.Billing_sign,
Nama_Dokter: dokterMap[b.ID_Billing],
}
result = append(result, item)
}
return result, nil
}
+133
View File
@@ -0,0 +1,133 @@
package services
import (
"backendcareit/database"
"backendcareit/models"
"gorm.io/gorm"
)
// Ambil tarif BPJS untuk rawat inap yaa
func GetTarifBPJSRawatInap() ([]models.TarifBPJSRawatInap, error) {
var data []models.TarifBPJSRawatInap
if err := database.DB.Model(&models.TarifBPJSRawatInap{}).Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
func GetTarifBPJSRawatInapByKode(kode string) (*models.TarifBPJSRawatInap, error) {
var data models.TarifBPJSRawatInap
if err := database.DB.Model(&models.TarifBPJSRawatInap{}).Where("ID_INACBG_RI = ?", kode).First(&data).Error; err != nil {
return nil, err
}
return &data, nil
}
// Ngambil tarif untuk pasien rawat jalan
func GetTarifBPJSRawatJalan() ([]models.TarifBPJSRawatJalan, error) {
var data []models.TarifBPJSRawatJalan
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
func GetTarifBPJSRawatJalanByKode(kode string) (*models.TarifBPJSRawatJalan, error) {
var data models.TarifBPJSRawatJalan
if err := database.DB.Where("ID_INACBG_RJ = ?", kode).First(&data).Error; err != nil {
return nil, err
}
return &data, nil
}
// Ambil tarif rumah sakit aja bro
func GetTarifRS() ([]models.TarifRS, error) {
var data []models.TarifRS
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
func GetTarifRSByKode(kode string) (*models.TarifRS, error) {
var data models.TarifRS
if err := database.DB.Where("ID_Tarif_RS = ?", kode).First(&data).Error; err != nil {
return nil, err
}
return &data, nil
}
func GetTarifRSByKategori(kategori string) ([]models.TarifRS, error) {
var data []models.TarifRS
if err := database.DB.Where("Kategori_RS = ?", kategori).Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
func IsNotFound(err error) bool {
return err == gorm.ErrRecordNotFound
}
// Ambil data ICD9 - kode diagnosa versi lama
func GetICD9() ([]models.ICD9, error) {
var data []models.ICD9
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
// Ambil data ICD10 - kode diagnosa versi baru
func GetICD10() ([]models.ICD10, error) {
var data []models.ICD10
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
// Ambil daftar semua ruangan di RS
func GetRuangan() ([]models.Ruangan, error) {
var data []models.Ruangan
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
// GetRuanganWithPasien - Get ruangan yang memiliki minimal 1 pasien
func GetRuanganWithPasien(db *gorm.DB) ([]models.Ruangan, error) {
var data []models.Ruangan
// JOIN dengan pasien table dan filter yang punya pasien
if err := db.
Distinct("ruangan.*").
Table("ruangan").
Joins("INNER JOIN pasien ON ruangan.\"Nama_Ruangan\" = pasien.\"Ruangan\"").
Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}
// Ambil list semua dokter yang ada
func GetDokter() ([]models.Dokter, error) {
var data []models.Dokter
if err := database.DB.Find(&data).Error; err != nil {
return nil, err
}
return data, nil
}