feat(BE-59,60,61): build stock transfer API with validation and audit log

- Implement CreateOne for stock transfer with multi-delivery and validation
- Preload warehouse, location, and area relations in transfer response
- Add audit log for transfer
- Improve transaction handling and error management
This commit is contained in:
aguhh18
2025-10-15 22:25:50 +07:00
parent d1b377ddac
commit 4107cf19ec
10 changed files with 219 additions and 77 deletions
@@ -13,8 +13,8 @@ type TransferBaseDTO struct {
Id uint64 `json:"id"`
TransferReason string `json:"transfer_reason"`
TransferDate string `json:"transfer_date"`
SourceWarehouse *WarehouseSimpleDTO `json:"source_warehouse,omitempty"`
DestinationWarehouse *WarehouseSimpleDTO `json:"destination_warehouse,omitempty"`
SourceWarehouse *WarehouseDetailDTO `json:"source_warehouse,omitempty"`
DestinationWarehouse *WarehouseDetailDTO `json:"destination_warehouse,omitempty"`
}
// Only id and name for warehouse simple view
@@ -23,6 +23,24 @@ type WarehouseSimpleDTO struct {
Name string `json:"name"`
}
type AreaDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
}
type LocationDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
Area *AreaDTO `json:"area"`
}
type WarehouseDetailDTO struct {
Id uint `json:"id"`
Name string `json:"name"`
Location *LocationDTO `json:"location"`
Area *AreaDTO `json:"area"`
}
type TransferListDTO struct {
TransferBaseDTO
CreatedUser *userDTO.UserBaseDTO `json:"created_user,omitempty"`
@@ -45,7 +63,6 @@ type TransferDetailItemDTO struct {
Quantity float64 `json:"quantity"`
BeforeQuantity float64 `json:"before_quantity"`
AfterQuantity float64 `json:"after_quantity"`
Note string `json:"note"`
}
// Delivery ekspedisi
@@ -58,7 +75,6 @@ type TransferDeliveryDTO struct {
DocumentPath string `json:"document_path"`
ShippingCostItem float64 `json:"shipping_cost_item"`
ShippingCostTotal float64 `json:"shipping_cost_total"`
Note string `json:"note"`
Items []TransferDeliveryItemDTO `json:"items"`
}
@@ -71,19 +87,14 @@ type TransferDeliveryItemDTO struct {
// === Mapper Functions ===
func ToTransferBaseDTO(e entity.StockTransfer) TransferBaseDTO {
var sourceWarehouse *WarehouseSimpleDTO
var sourceWarehouse *WarehouseDetailDTO
if e.FromWarehouse != nil && e.FromWarehouse.Id != 0 {
sourceWarehouse = &WarehouseSimpleDTO{
Id: e.FromWarehouse.Id,
Name: e.FromWarehouse.Name,
}
sourceWarehouse = toWarehouseDetailDTO(e.FromWarehouse)
}
var destinationWarehouse *WarehouseSimpleDTO
var destinationWarehouse *WarehouseDetailDTO
if e.ToWarehouse != nil && e.ToWarehouse.Id != 0 {
destinationWarehouse = &WarehouseSimpleDTO{
Id: e.ToWarehouse.Id,
Name: e.ToWarehouse.Name,
}
destinationWarehouse = toWarehouseDetailDTO(e.ToWarehouse)
}
return TransferBaseDTO{
Id: e.Id,
@@ -94,6 +105,40 @@ func ToTransferBaseDTO(e entity.StockTransfer) TransferBaseDTO {
}
}
func toAreaDTO(a *entity.Area) *AreaDTO {
if a == nil {
return nil
}
return &AreaDTO{
Id: a.Id,
Name: a.Name,
}
}
func toLocationDTO(l *entity.Location) *LocationDTO {
if l == nil {
return nil
}
// Area selalu diisi jika l.Area ada
return &LocationDTO{
Id: l.Id,
Name: l.Name,
Area: toAreaDTO(&l.Area),
}
}
func toWarehouseDetailDTO(w *entity.Warehouse) *WarehouseDetailDTO {
if w == nil {
return nil
}
return &WarehouseDetailDTO{
Id: w.Id,
Name: w.Name,
Location: toLocationDTO(w.Location),
Area: toAreaDTO(&w.Area),
}
}
func ToTransferListDTO(e entity.StockTransfer) TransferListDTO {
var createdUser *userDTO.UserBaseDTO
if e.CreatedUser != nil {
@@ -104,12 +149,9 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO {
var details []TransferDetailItemDTO
for _, d := range e.Details {
details = append(details, TransferDetailItemDTO{
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
BeforeQuantity: d.BeforeQuantity,
AfterQuantity: d.AfterQuantity,
Note: d.Note,
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
})
}
// Map deliveries
@@ -133,7 +175,6 @@ func ToTransferListDTO(e entity.StockTransfer) TransferListDTO {
DocumentPath: del.DocumentPath,
ShippingCostItem: del.ShippingCostItem,
ShippingCostTotal: del.ShippingCostTotal,
Note: del.Note,
Items: items,
})
}
@@ -160,12 +201,9 @@ func ToTransferDetailDTO(e entity.StockTransfer) TransferDetailDTO {
var details []TransferDetailItemDTO
for _, d := range e.Details {
details = append(details, TransferDetailItemDTO{
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
BeforeQuantity: d.BeforeQuantity,
AfterQuantity: d.AfterQuantity,
Note: d.Note,
Id: d.Id,
ProductId: d.ProductId,
Quantity: d.Quantity,
})
}
// Map deliveries
@@ -180,7 +218,6 @@ func ToTransferDetailDTO(e entity.StockTransfer) TransferDetailDTO {
DocumentPath: del.DocumentPath,
ShippingCostItem: del.ShippingCostItem,
ShippingCostTotal: del.ShippingCostTotal,
Note: del.Note,
})
}
return TransferDetailDTO{