216 lines
6.4 KiB
Go
216 lines
6.4 KiB
Go
package http
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"net/http"
|
|
"strconv"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"primaya-api/cpone-middleware/internal/repository"
|
|
)
|
|
|
|
func (h Handler) AddLabServices(w http.ResponseWriter, r *http.Request) {
|
|
payload, _ := decodeJSONObject(r)
|
|
params, validationMessage := validateAddLabServices(payload)
|
|
if validationMessage != "" {
|
|
writeLabResponse(w, http.StatusUnprocessableEntity, false, "validation_error", validationMessage, nil)
|
|
return
|
|
}
|
|
|
|
outcome, err := h.labRepository(r.Context()).AddLabServices(r.Context(), r.PathValue("regId"), params)
|
|
if err != nil {
|
|
if validationErr, ok := asValidationError(err); ok {
|
|
writeLabResponse(w, http.StatusUnprocessableEntity, false, "validation_error", validationErr, nil)
|
|
return
|
|
}
|
|
writeLabResponse(w, http.StatusInternalServerError, false, "error", err.Error(), nil)
|
|
return
|
|
}
|
|
|
|
writeLabResponse(w, outcome.HTTPStatus, outcome.Success, outcome.Status, outcome.Message, outcome.Data)
|
|
}
|
|
|
|
func validateAddLabServices(payload map[string]any) (repository.AddLabServicesParams, string) {
|
|
value, exists := payload["layanan"]
|
|
if !exists || isLaravelEmpty(value) {
|
|
return repository.AddLabServicesParams{}, "Minimal satu layanan wajib diisi"
|
|
}
|
|
items, ok := value.([]any)
|
|
if !ok {
|
|
return repository.AddLabServicesParams{}, "The layanan must be an array."
|
|
}
|
|
if len(items) == 0 {
|
|
return repository.AddLabServicesParams{}, "Minimal satu layanan wajib diisi"
|
|
}
|
|
|
|
services := make([]repository.RequestedLabService, len(items))
|
|
seenIDs := make(map[string]struct{}, len(items))
|
|
for index, rawItem := range items {
|
|
item, _ := rawItem.(map[string]any)
|
|
serviceID, message := requiredNestedString(item, "layanan_id", index, 50, "Layanan ID wajib diisi")
|
|
if message != "" {
|
|
return repository.AddLabServicesParams{}, message
|
|
}
|
|
if _, duplicate := seenIDs[serviceID]; duplicate {
|
|
return repository.AddLabServicesParams{}, "Layanan yang sama tidak boleh dikirim lebih dari satu kali"
|
|
}
|
|
seenIDs[serviceID] = struct{}{}
|
|
services[index].LayananID = serviceID
|
|
}
|
|
|
|
for index, rawItem := range items {
|
|
item, _ := rawItem.(map[string]any)
|
|
services[index].Jumlah = 1
|
|
if value, present := nullableValue(item, "jumlah"); present {
|
|
quantity, valid := parseInteger(value)
|
|
if !valid {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "jumlah", "must be an integer")
|
|
}
|
|
if quantity < 1 {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "jumlah", "must be at least 1")
|
|
}
|
|
if quantity > 100 {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "jumlah", "must not be greater than 100")
|
|
}
|
|
services[index].Jumlah = int(quantity)
|
|
}
|
|
}
|
|
|
|
for index, rawItem := range items {
|
|
item, _ := rawItem.(map[string]any)
|
|
if value, present := nullableValue(item, "cito"); present {
|
|
cito, valid := parseLaravelBoolean(value)
|
|
if !valid {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "cito", "field must be true or false")
|
|
}
|
|
services[index].Cito = cito
|
|
}
|
|
}
|
|
|
|
for index, rawItem := range items {
|
|
item, _ := rawItem.(map[string]any)
|
|
if value, present := nullableValue(item, "markup_cito"); present {
|
|
markup, valid := parseNumeric(value)
|
|
if !valid {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "markup_cito", "must be a number")
|
|
}
|
|
if markup < 0 {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "markup_cito", "must be at least 0")
|
|
}
|
|
if markup > 100 {
|
|
return repository.AddLabServicesParams{}, nestedMessage(index, "markup_cito", "must not be greater than 100")
|
|
}
|
|
services[index].MarkupCito = markup
|
|
}
|
|
if !services[index].Cito {
|
|
services[index].MarkupCito = 0
|
|
}
|
|
}
|
|
|
|
for index, rawItem := range items {
|
|
item, _ := rawItem.(map[string]any)
|
|
notes, _, message := optionalNestedString(item, "catatan", index, 500)
|
|
if message != "" {
|
|
return repository.AddLabServicesParams{}, message
|
|
}
|
|
services[index].Catatan = notes
|
|
}
|
|
|
|
channel, channelPresent, message := optionalString(payload, "channel", 50)
|
|
if message != "" {
|
|
return repository.AddLabServicesParams{}, message
|
|
}
|
|
if !channelPresent {
|
|
channel = "CPONE"
|
|
}
|
|
channel = strings.ToUpper(strings.TrimSpace(channel))
|
|
|
|
return repository.AddLabServicesParams{Layanan: services, Channel: channel}, ""
|
|
}
|
|
|
|
func requiredNestedString(item map[string]any, key string, index int, maxLength int, requiredMessage string) (string, string) {
|
|
value, exists := item[key]
|
|
if !exists || isLaravelEmpty(value) {
|
|
return "", requiredMessage
|
|
}
|
|
text, ok := value.(string)
|
|
if !ok {
|
|
return "", nestedMessage(index, key, "must be a string")
|
|
}
|
|
if utf8.RuneCountInString(text) > maxLength {
|
|
return "", nestedMessage(index, key, fmt.Sprintf("must not be greater than %d characters", maxLength))
|
|
}
|
|
return text, ""
|
|
}
|
|
|
|
func optionalNestedString(item map[string]any, key string, index int, maxLength int) (string, bool, string) {
|
|
value, present := nullableValue(item, key)
|
|
if !present {
|
|
return "", false, ""
|
|
}
|
|
text, ok := value.(string)
|
|
if !ok {
|
|
return "", false, nestedMessage(index, key, "must be a string")
|
|
}
|
|
if utf8.RuneCountInString(text) > maxLength {
|
|
return "", false, nestedMessage(index, key, fmt.Sprintf("must not be greater than %d characters", maxLength))
|
|
}
|
|
return text, true, ""
|
|
}
|
|
|
|
func nestedMessage(index int, key string, ruleMessage string) string {
|
|
attribute := fmt.Sprintf("layanan.%d.%s", index, strings.ReplaceAll(key, "_", " "))
|
|
if strings.HasPrefix(ruleMessage, "field ") {
|
|
return fmt.Sprintf("The %s %s.", attribute, ruleMessage)
|
|
}
|
|
return fmt.Sprintf("The %s %s.", attribute, ruleMessage)
|
|
}
|
|
|
|
func parseLaravelBoolean(value any) (bool, bool) {
|
|
switch typed := value.(type) {
|
|
case bool:
|
|
return typed, true
|
|
case json.Number:
|
|
if typed.String() == "1" {
|
|
return true, true
|
|
}
|
|
if typed.String() == "0" {
|
|
return false, true
|
|
}
|
|
case string:
|
|
if typed == "1" {
|
|
return true, true
|
|
}
|
|
if typed == "0" {
|
|
return false, true
|
|
}
|
|
}
|
|
return false, false
|
|
}
|
|
|
|
func parseNumeric(value any) (float64, bool) {
|
|
var text string
|
|
switch typed := value.(type) {
|
|
case json.Number:
|
|
text = typed.String()
|
|
case string:
|
|
text = strings.TrimSpace(typed)
|
|
default:
|
|
return 0, false
|
|
}
|
|
parsed, err := strconv.ParseFloat(text, 64)
|
|
return parsed, err == nil && !math.IsInf(parsed, 0) && !math.IsNaN(parsed)
|
|
}
|
|
|
|
func asValidationError(err error) (string, bool) {
|
|
var validationErr repository.ValidationError
|
|
if errors.As(err, &validationErr) {
|
|
return validationErr.Message, true
|
|
}
|
|
return "", false
|
|
}
|