Files
lti-api/internal/modules/expenses/dto/expense.dto.go
T

347 lines
11 KiB
Go

package dto
import (
"encoding/json"
"fmt"
"time"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
nonstockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/dto"
supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto"
userDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
)
// === Base DTO ===
type ExpenseBaseDTO struct {
Id uint64 `json:"id"`
ReferenceNumber string `json:"reference_number"`
PoNumber *string `json:"po_number"`
Category string `json:"category"`
Documents []string `json:"documents,omitempty"`
ExpenseDate time.Time `json:"expense_date"`
GrandTotal float64 `json:"grand_total"`
Location *locationDTO.LocationBaseDTO `json:"location,omitempty"`
}
// === List DTO (untuk GetAll) ===
type ExpenseListDTO struct {
ExpenseBaseDTO
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LatestApproval *approvalDTO.ApprovalBaseDTO `json:"latest_approval,omitempty"`
}
// === Detail DTO (untuk GetOne) ===
type ExpenseDetailDTO struct {
ExpenseBaseDTO
Supplier *supplierDTO.SupplierBaseDTO `json:"supplier,omitempty"`
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
Kandangs []KandangGroupDTO `json:"kandangs,omitempty"`
TotalPengajuan float64 `json:"total_pengajuan"`
TotalRealisasi float64 `json:"total_realisasi"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
LatestApproval *approvalDTO.ApprovalBaseDTO `json:"latest_approval,omitempty"`
}
// === Nested DTO ===
type ExpenseNonstockDTO struct {
Id uint64 `json:"id"`
Qty float64 `json:"qty"`
UnitPrice float64 `json:"unit_price"`
TotalPrice float64 `json:"total_price"`
Note *string `json:"note,omitempty"`
Nonstock *nonstockDTO.NonstockBaseDTO `json:"nonstock,omitempty"`
ProjectFlockKandang *ProjectFlockKandangNestedDTO `json:"project_flock_kandang,omitempty"`
Realization *ExpenseRealizationDTO `json:"realization,omitempty"`
}
type ProjectFlockKandangNestedDTO struct {
Id uint64 `json:"id"`
KandangId uint64 `json:"kandang_id"`
}
type ExpenseRealizationDTO struct {
Id uint64 `json:"id"`
RealizationQty float64 `json:"realization_qty"`
RealizationUnitPrice float64 `json:"realization_unit_price"`
RealizationTotalPrice float64 `json:"realization_total_price"`
RealizationDate time.Time `json:"realization_date"`
Note *string `json:"note,omitempty"`
}
type RealizationOnlyDTO struct {
Id uint64 `json:"id"`
RealizationQty float64 `json:"realization_qty"`
RealizationUnitPrice float64 `json:"realization_unit_price"`
RealizationTotalPrice float64 `json:"realization_total_price"`
RealizationDate time.Time `json:"realization_date"`
Note *string `json:"note,omitempty"`
Nonstock *nonstockDTO.NonstockBaseDTO `json:"nonstock,omitempty"`
ProjectFlockKandang *ProjectFlockKandangNestedDTO `json:"project_flock_kandang,omitempty"`
}
type KandangDTO struct {
Id uint64 `json:"id"`
KandangId uint64 `json:"kandang_id"`
Name string `json:"name,omitempty"`
}
type KandangGroupDTO struct {
Id uint64 `json:"id"`
KandangId uint64 `json:"kandang_id"`
Name string `json:"name,omitempty"`
Pengajuans []ExpenseNonstockDTO `json:"pengajuans,omitempty"`
Realisasi []RealizationOnlyDTO `json:"realisasi,omitempty"`
}
// === Helper Functions ===
func getStringValue(s *string) string {
if s == nil {
return ""
}
return *s
}
// === Mapper Functions ===
func ToExpenseBaseDTO(e *entity.Expense) ExpenseBaseDTO {
var documents []string
var location *locationDTO.LocationBaseDTO
// Parse document paths from JSON if available
if e.DocumentPath.Valid && e.DocumentPath.String != "" {
if err := json.Unmarshal([]byte(e.DocumentPath.String), &documents); err == nil {
// Successfully parsed documents
}
}
// Get location from the first kandang if available
if len(e.Nonstocks) > 0 && e.Nonstocks[0].ProjectFlockKandang != nil {
if e.Nonstocks[0].ProjectFlockKandang.Kandang.Location.Id != 0 {
mapped := locationDTO.ToLocationBaseDTO(e.Nonstocks[0].ProjectFlockKandang.Kandang.Location)
location = &mapped
}
}
return ExpenseBaseDTO{
Id: e.Id,
ReferenceNumber: getStringValue(e.ReferenceNumber),
PoNumber: e.PoNumber, // Keep as pointer to allow null in JSON
Category: e.Category,
Documents: documents,
ExpenseDate: e.ExpenseDate,
GrandTotal: e.GrandTotal,
Location: location,
}
}
func ToExpenseListDTO(e *entity.Expense) ExpenseListDTO {
var createdUser *userDTO.UserBaseDTO
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
mapped := userDTO.ToUserBaseDTO(*e.CreatedUser)
createdUser = &mapped
}
var latestApproval *approvalDTO.ApprovalBaseDTO
if e.LatestApproval != nil {
mapped := approvalDTO.ToApprovalDTO(*e.LatestApproval)
latestApproval = &mapped
}
return ExpenseListDTO{
ExpenseBaseDTO: ToExpenseBaseDTO(e),
CreatedUser: createdUser,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
LatestApproval: latestApproval,
}
}
func ToExpenseListDTOs(expenses []entity.Expense) []ExpenseListDTO {
result := make([]ExpenseListDTO, len(expenses))
for i, expense := range expenses {
result[i] = ToExpenseListDTO(&expense)
}
return result
}
func ToExpenseDetailDTO(e *entity.Expense) ExpenseDetailDTO {
var createdUser *userDTO.UserBaseDTO
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
mapped := userDTO.ToUserBaseDTO(*e.CreatedUser)
createdUser = &mapped
}
var supplier *supplierDTO.SupplierBaseDTO
if e.Supplier != nil && e.Supplier.Id != 0 {
mapped := supplierDTO.ToSupplierBaseDTO(*e.Supplier)
supplier = &mapped
}
var latestApproval *approvalDTO.ApprovalBaseDTO
if e.LatestApproval != nil {
mapped := approvalDTO.ToApprovalDTO(*e.LatestApproval)
latestApproval = &mapped
}
var pengajuans []ExpenseNonstockDTO
var realisasi []RealizationOnlyDTO
if len(e.Nonstocks) > 0 {
pengajuans = make([]ExpenseNonstockDTO, 0)
realisasi = make([]RealizationOnlyDTO, 0)
for _, ns := range e.Nonstocks {
// Create DTO without realization for pengajuans
pengajuanDTO := ToExpenseNonstockDTO(ns)
pengajuanDTO.Realization = nil // Remove realization from pengajuan
pengajuans = append(pengajuans, pengajuanDTO)
// Create separate DTO with realization data if it exists
if ns.Realization != nil && ns.Realization.Id != 0 {
// Create realization DTO with only realization data
var nonstock *nonstockDTO.NonstockBaseDTO
if ns.Nonstock != nil && ns.Nonstock.Id != 0 {
mapped := nonstockDTO.ToNonstockBaseDTO(*ns.Nonstock)
nonstock = &mapped
}
var projectFlockKandang *ProjectFlockKandangNestedDTO
if ns.ProjectFlockKandang != nil && ns.ProjectFlockKandang.Id != 0 {
projectFlockKandang = &ProjectFlockKandangNestedDTO{
Id: uint64(ns.ProjectFlockKandang.Id),
KandangId: uint64(ns.ProjectFlockKandang.KandangId),
}
}
realisasiDTO := RealizationOnlyDTO{
Id: ns.Realization.Id,
RealizationQty: ns.Realization.RealizationQty,
RealizationUnitPrice: ns.Realization.RealizationUnitPrice,
RealizationTotalPrice: ns.Realization.RealizationTotalPrice,
RealizationDate: ns.Realization.RealizationDate,
Note: ns.Realization.Note,
Nonstock: nonstock,
ProjectFlockKandang: projectFlockKandang,
}
realisasi = append(realisasi, realisasiDTO)
}
}
}
// Calculate total pengajuan and realisasi
var totalPengajuan float64
for _, p := range pengajuans {
totalPengajuan += p.TotalPrice
}
var totalRealisasi float64
for _, r := range realisasi {
totalRealisasi += r.RealizationTotalPrice
}
// Group pengajuans and realisasi by kandang
kandangMap := make(map[uint64]*KandangGroupDTO)
// Process pengajuans
for _, p := range pengajuans {
if p.ProjectFlockKandang != nil {
kandangId := p.ProjectFlockKandang.KandangId
if kandangMap[kandangId] == nil {
kandangMap[kandangId] = &KandangGroupDTO{
Id: p.ProjectFlockKandang.Id,
KandangId: kandangId,
Name: fmt.Sprintf("Kandang %d", kandangId),
}
}
kandangMap[kandangId].Pengajuans = append(kandangMap[kandangId].Pengajuans, p)
}
}
// Process realisasi
for _, r := range realisasi {
if r.ProjectFlockKandang != nil {
kandangId := r.ProjectFlockKandang.KandangId
if kandangMap[kandangId] == nil {
kandangMap[kandangId] = &KandangGroupDTO{
Id: r.ProjectFlockKandang.Id,
KandangId: kandangId,
Name: fmt.Sprintf("Kandang %d", kandangId),
}
}
kandangMap[kandangId].Realisasi = append(kandangMap[kandangId].Realisasi, r)
}
}
// Convert map to slice
var kandangs []KandangGroupDTO
for _, k := range kandangMap {
kandangs = append(kandangs, *k)
}
return ExpenseDetailDTO{
ExpenseBaseDTO: ToExpenseBaseDTO(e),
Supplier: supplier,
CreatedUser: createdUser,
Kandangs: kandangs,
TotalPengajuan: totalPengajuan,
TotalRealisasi: totalRealisasi,
CreatedAt: e.CreatedAt,
UpdatedAt: e.UpdatedAt,
LatestApproval: latestApproval,
}
}
func ToExpenseNonstockDTO(ns entity.ExpenseNonstock) ExpenseNonstockDTO {
var nonstock *nonstockDTO.NonstockBaseDTO
if ns.Nonstock != nil && ns.Nonstock.Id != 0 {
mapped := nonstockDTO.ToNonstockBaseDTO(*ns.Nonstock)
nonstock = &mapped
}
var projectFlockKandang *ProjectFlockKandangNestedDTO
if ns.ProjectFlockKandang != nil && ns.ProjectFlockKandang.Id != 0 {
projectFlockKandang = &ProjectFlockKandangNestedDTO{
Id: uint64(ns.ProjectFlockKandang.Id),
KandangId: uint64(ns.ProjectFlockKandang.KandangId),
}
}
var realization *ExpenseRealizationDTO
if ns.Realization != nil && ns.Realization.Id != 0 {
mapped := ToExpenseRealizationDTO(*ns.Realization)
realization = &mapped
}
return ExpenseNonstockDTO{
Id: ns.Id,
Qty: ns.Qty,
UnitPrice: ns.UnitPrice,
TotalPrice: ns.TotalPrice,
Note: ns.Note,
Nonstock: nonstock,
ProjectFlockKandang: projectFlockKandang,
Realization: realization,
}
}
func ToExpenseRealizationDTO(r entity.ExpenseRealization) ExpenseRealizationDTO {
return ExpenseRealizationDTO{
Id: r.Id,
RealizationQty: r.RealizationQty,
RealizationUnitPrice: r.RealizationUnitPrice,
RealizationTotalPrice: r.RealizationTotalPrice,
RealizationDate: r.RealizationDate,
Note: r.Note,
}
}