mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
380 lines
12 KiB
Go
380 lines
12 KiB
Go
package dto
|
|
|
|
import (
|
|
"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"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
)
|
|
|
|
// === DTO Structs ===
|
|
|
|
type ExpenseRelationDTO struct {
|
|
Id uint64 `json:"id"`
|
|
PoNumber string `json:"po_number"`
|
|
TransactionDate time.Time `json:"transaction_date"`
|
|
}
|
|
|
|
type ExpenseBaseDTO struct {
|
|
Id uint64 `json:"id"`
|
|
ReferenceNumber string `json:"reference_number"`
|
|
PoNumber string `json:"po_number"`
|
|
Category string `json:"category"`
|
|
Supplier *supplierDTO.SupplierRelationDTO `json:"supplier,omitempty"`
|
|
RealizationDate *time.Time `json:"realization_date,omitempty"`
|
|
TransactionDate time.Time `json:"transaction_date"`
|
|
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
|
IsPaid bool `json:"is_paid"`
|
|
}
|
|
|
|
type ExpenseListDTO struct {
|
|
ExpenseBaseDTO
|
|
GrandTotal float64 `json:"grand_total"`
|
|
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval,omitempty"`
|
|
}
|
|
|
|
type ExpenseDetailDTO struct {
|
|
ExpenseBaseDTO
|
|
Documents []DocumentDTO `json:"documents"`
|
|
RealizationDocs []DocumentDTO `json:"realization_docs"`
|
|
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"`
|
|
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
|
LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval,omitempty"`
|
|
}
|
|
|
|
type ExpenseNonstockDTO struct {
|
|
Id uint64 `json:"id"`
|
|
ExpenseId *uint64 `json:"expense_id,omitempty"`
|
|
ProjectFlockKandangId *uint64 `json:"project_flock_kandang_id,omitempty"`
|
|
KandangId *uint64 `json:"kandang_id,omitempty"`
|
|
NonstockId *uint64 `json:"nonstock_id,omitempty"`
|
|
Qty float64 `json:"qty"`
|
|
Price float64 `json:"price"`
|
|
Notes string `json:"notes"`
|
|
Nonstock *nonstockDTO.NonstockRelationDTO `json:"nonstock,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
type ExpenseRealizationDTO struct {
|
|
Id uint64 `json:"id"`
|
|
ExpenseNonstockId *uint64 `json:"expense_nonstock_id,omitempty"`
|
|
Qty float64 `json:"qty"`
|
|
Price float64 `json:"price"`
|
|
Notes string `json:"notes"`
|
|
Nonstock *nonstockDTO.NonstockRelationDTO `json:"nonstock,omitempty"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
}
|
|
|
|
type KandangGroupDTO struct {
|
|
Id uint64 `json:"id"`
|
|
Name string `json:"name,omitempty"`
|
|
Pengajuans []ExpenseNonstockDTO `json:"pengajuans,omitempty"`
|
|
Realisasi []ExpenseRealizationDTO `json:"realisasi,omitempty"`
|
|
}
|
|
|
|
type DocumentDTO struct {
|
|
ID uint64 `json:"id"`
|
|
Path string `json:"path"`
|
|
Name string `json:"name"`
|
|
}
|
|
|
|
// === MAPPERS ===
|
|
|
|
func ToExpenseRelationDTO(e entity.Expense) ExpenseRelationDTO {
|
|
return ExpenseRelationDTO{
|
|
Id: e.Id,
|
|
PoNumber: e.PoNumber,
|
|
TransactionDate: e.TransactionDate,
|
|
}
|
|
}
|
|
|
|
func ToExpenseBaseDTO(e *entity.Expense) ExpenseBaseDTO {
|
|
var location *locationDTO.LocationRelationDTO
|
|
var supplier *supplierDTO.SupplierRelationDTO
|
|
|
|
var realizationDate *time.Time
|
|
if !e.RealizationDate.IsZero() {
|
|
realizationDate = &e.RealizationDate
|
|
}
|
|
|
|
if e.Location != nil && e.Location.Id != 0 {
|
|
mapped := locationDTO.ToLocationRelationDTO(*e.Location)
|
|
location = &mapped
|
|
}
|
|
|
|
if e.Supplier != nil && e.Supplier.Id != 0 {
|
|
mapped := supplierDTO.ToSupplierRelationDTO(*e.Supplier)
|
|
supplier = &mapped
|
|
}
|
|
|
|
return ExpenseBaseDTO{
|
|
Id: e.Id,
|
|
ReferenceNumber: e.ReferenceNumber,
|
|
PoNumber: e.PoNumber,
|
|
Category: e.Category,
|
|
Supplier: supplier,
|
|
RealizationDate: realizationDate,
|
|
TransactionDate: e.TransactionDate,
|
|
Location: location,
|
|
IsPaid: e.IsPaid,
|
|
}
|
|
}
|
|
|
|
func ToExpenseListDTO(e entity.Expense) ExpenseListDTO {
|
|
var createdUser *userDTO.UserRelationDTO
|
|
if e.CreatedUser.Id != 0 {
|
|
mapped := userDTO.ToUserRelationDTO(*e.CreatedUser)
|
|
createdUser = &mapped
|
|
}
|
|
|
|
var latestApproval *approvalDTO.ApprovalRelationDTO
|
|
if e.LatestApproval != nil {
|
|
mapped := approvalDTO.ToApprovalDTO(*e.LatestApproval)
|
|
latestApproval = &mapped
|
|
}
|
|
|
|
grandTotal := calculateGrandTotal(&e)
|
|
|
|
return ExpenseListDTO{
|
|
ExpenseBaseDTO: ToExpenseBaseDTO(&e),
|
|
GrandTotal: grandTotal,
|
|
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 documents []DocumentDTO
|
|
var realizationDocs []DocumentDTO
|
|
var createdUser *userDTO.UserRelationDTO
|
|
if e.CreatedUser != nil && e.CreatedUser.Id != 0 {
|
|
mapped := userDTO.ToUserRelationDTO(*e.CreatedUser)
|
|
createdUser = &mapped
|
|
}
|
|
|
|
var latestApproval *approvalDTO.ApprovalRelationDTO
|
|
if e.LatestApproval != nil {
|
|
mapped := approvalDTO.ToApprovalDTO(*e.LatestApproval)
|
|
latestApproval = &mapped
|
|
}
|
|
|
|
var pengajuans []ExpenseNonstockDTO
|
|
var realisasi []ExpenseRealizationDTO
|
|
|
|
for _, doc := range e.Documents {
|
|
documents = append(documents, DocumentDTO{
|
|
ID: uint64(doc.Id),
|
|
Path: doc.Path,
|
|
Name: doc.Name,
|
|
})
|
|
}
|
|
|
|
for _, doc := range e.RealizationDocuments {
|
|
realizationDocs = append(realizationDocs, DocumentDTO{
|
|
ID: uint64(doc.Id),
|
|
Path: doc.Path,
|
|
Name: doc.Name,
|
|
})
|
|
}
|
|
|
|
if len(e.Nonstocks) > 0 {
|
|
pengajuans = make([]ExpenseNonstockDTO, 0)
|
|
realisasi = make([]ExpenseRealizationDTO, 0)
|
|
|
|
for _, ns := range e.Nonstocks {
|
|
pengajuanDTO := ToExpenseNonstockDTO(ns)
|
|
pengajuans = append(pengajuans, pengajuanDTO)
|
|
|
|
if ns.Realization != nil {
|
|
var nonstock *nonstockDTO.NonstockRelationDTO
|
|
if ns.Nonstock != nil && ns.Nonstock.Id != 0 {
|
|
mapped := nonstockDTO.ToNonstockRelationDTO(*ns.Nonstock)
|
|
nonstock = &mapped
|
|
}
|
|
|
|
realisasiDTO := ExpenseRealizationDTO{
|
|
Id: ns.Realization.Id,
|
|
ExpenseNonstockId: ns.Realization.ExpenseNonstockId,
|
|
Qty: ns.Realization.Qty,
|
|
Price: ns.Realization.Price,
|
|
Notes: ns.Realization.Notes,
|
|
Nonstock: nonstock,
|
|
CreatedAt: ns.Realization.CreatedAt,
|
|
}
|
|
realisasi = append(realisasi, realisasiDTO)
|
|
}
|
|
}
|
|
}
|
|
|
|
var totalPengajuan float64
|
|
for _, p := range pengajuans {
|
|
totalPengajuan += p.Qty * p.Price
|
|
}
|
|
|
|
var totalRealisasi float64
|
|
for _, r := range realisasi {
|
|
totalRealisasi += r.Qty * r.Price
|
|
}
|
|
kandangs := ToKandangGroupDTO(pengajuans, realisasi, e.Nonstocks)
|
|
|
|
return ExpenseDetailDTO{
|
|
ExpenseBaseDTO: ToExpenseBaseDTO(e),
|
|
Documents: documents,
|
|
RealizationDocs: realizationDocs,
|
|
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.NonstockRelationDTO
|
|
if ns.Nonstock != nil && ns.Nonstock.Id != 0 {
|
|
mapped := nonstockDTO.ToNonstockRelationDTO(*ns.Nonstock)
|
|
nonstock = &mapped
|
|
}
|
|
|
|
return ExpenseNonstockDTO{
|
|
Id: ns.Id,
|
|
ExpenseId: ns.ExpenseId,
|
|
ProjectFlockKandangId: ns.ProjectFlockKandangId,
|
|
KandangId: ns.KandangId,
|
|
NonstockId: ns.NonstockId,
|
|
Qty: ns.Qty,
|
|
Price: ns.Price,
|
|
Notes: ns.Notes,
|
|
Nonstock: nonstock,
|
|
CreatedAt: ns.CreatedAt,
|
|
}
|
|
}
|
|
|
|
func ToKandangGroupDTO(pengajuans []ExpenseNonstockDTO, realisasi []ExpenseRealizationDTO, nonstocks []entity.ExpenseNonstock) []KandangGroupDTO {
|
|
kandangMap := make(map[uint64]*KandangGroupDTO)
|
|
var directPengajuans []ExpenseNonstockDTO
|
|
var directRealisasi []ExpenseRealizationDTO
|
|
|
|
for _, p := range pengajuans {
|
|
var kandangId uint64
|
|
var kandangName string
|
|
|
|
if p.KandangId != nil {
|
|
kandangId = *p.KandangId
|
|
for _, ns := range nonstocks {
|
|
if ns.Id == p.Id && ns.Kandang != nil {
|
|
kandangName = ns.Kandang.Name
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if kandangId > 0 {
|
|
|
|
if kandangMap[kandangId] == nil {
|
|
kandangMap[kandangId] = &KandangGroupDTO{
|
|
Id: kandangId,
|
|
Name: kandangName,
|
|
Pengajuans: []ExpenseNonstockDTO{},
|
|
Realisasi: []ExpenseRealizationDTO{},
|
|
}
|
|
}
|
|
kandangMap[kandangId].Pengajuans = append(kandangMap[kandangId].Pengajuans, p)
|
|
} else {
|
|
|
|
directPengajuans = append(directPengajuans, p)
|
|
}
|
|
}
|
|
|
|
for _, r := range realisasi {
|
|
var kandangId uint64
|
|
var kandangName string
|
|
|
|
for _, ns := range nonstocks {
|
|
if ns.Realization != nil && ns.Realization.Id == r.Id && ns.Kandang != nil {
|
|
kandangId = uint64(ns.Kandang.Id)
|
|
kandangName = ns.Kandang.Name
|
|
break
|
|
}
|
|
}
|
|
|
|
if kandangId > 0 {
|
|
if kandangMap[kandangId] == nil {
|
|
kandangMap[kandangId] = &KandangGroupDTO{
|
|
Id: kandangId,
|
|
Name: kandangName,
|
|
Pengajuans: []ExpenseNonstockDTO{},
|
|
Realisasi: []ExpenseRealizationDTO{},
|
|
}
|
|
}
|
|
kandangMap[kandangId].Realisasi = append(kandangMap[kandangId].Realisasi, r)
|
|
} else {
|
|
directRealisasi = append(directRealisasi, r)
|
|
}
|
|
}
|
|
|
|
// If there are direct expenses (without kandang), add them as a special entry with id=0
|
|
if len(directPengajuans) > 0 || len(directRealisasi) > 0 {
|
|
kandangMap[0] = &KandangGroupDTO{
|
|
Id: 0,
|
|
|
|
Name: "",
|
|
Pengajuans: directPengajuans,
|
|
Realisasi: directRealisasi,
|
|
}
|
|
}
|
|
|
|
var kandangs []KandangGroupDTO
|
|
for _, k := range kandangMap {
|
|
kandangs = append(kandangs, *k)
|
|
}
|
|
|
|
return kandangs
|
|
}
|
|
|
|
func calculateGrandTotal(expense *entity.Expense) float64 {
|
|
|
|
useRealization := expense.LatestApproval != nil && expense.LatestApproval.StepNumber >= uint16(utils.ExpenseStepRealisasi)
|
|
|
|
if useRealization {
|
|
|
|
var total float64
|
|
for _, ns := range expense.Nonstocks {
|
|
if ns.Realization != nil {
|
|
total += ns.Realization.Qty * ns.Realization.Price
|
|
}
|
|
}
|
|
return total
|
|
}
|
|
|
|
var total float64
|
|
for _, ns := range expense.Nonstocks {
|
|
total += ns.Qty * ns.Price
|
|
}
|
|
return total
|
|
}
|