mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 690db8b485 | |||
| f836685253 | |||
| 5e9286428f | |||
| 61e15dd95d | |||
| 59d72f20b4 |
@@ -0,0 +1,2 @@
|
||||
UPDATE phases SET is_active = true
|
||||
WHERE id IN (2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26);
|
||||
@@ -0,0 +1,2 @@
|
||||
UPDATE phases SET is_active = false
|
||||
WHERE id IN (2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26);
|
||||
@@ -7,7 +7,20 @@ type RecordingStock struct {
|
||||
ProjectFlockKandangId *uint `gorm:"column:project_flock_kandang_id;index"`
|
||||
UsageQty *float64 `gorm:"column:usage_qty"`
|
||||
PendingQty *float64 `gorm:"column:pending_qty"`
|
||||
TotalPrice float64 `gorm:"-"`
|
||||
Allocations []RecordingStockAlloc `gorm:"-"`
|
||||
|
||||
Recording Recording `gorm:"foreignKey:RecordingId;references:Id"`
|
||||
ProductWarehouse ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||
}
|
||||
|
||||
type RecordingStockAlloc struct {
|
||||
SourceType string
|
||||
SourceId uint
|
||||
PrNumber string
|
||||
PoNumber string
|
||||
AdjNumber string
|
||||
Qty float64
|
||||
UnitPrice float64
|
||||
Subtotal float64
|
||||
}
|
||||
|
||||
@@ -1215,7 +1215,9 @@ func (s dailyChecklistService) AssignPhases(c *fiber.Ctx, id uint, req *validati
|
||||
}
|
||||
|
||||
if len(phaseIDs) > 0 {
|
||||
phases, err := s.PhaseRepo.GetByIDs(c.Context(), phaseIDs, nil)
|
||||
phases, err := s.PhaseRepo.GetByIDs(c.Context(), phaseIDs, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("is_active = true")
|
||||
})
|
||||
if err != nil {
|
||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Phase not found")
|
||||
|
||||
@@ -50,6 +50,7 @@ func (s phasesService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.
|
||||
|
||||
phasess, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||
db = s.withRelations(db)
|
||||
db = db.Where("is_active = true")
|
||||
if params.Search != "" {
|
||||
return db.Where("name ILIKE ?", "%"+params.Search+"%")
|
||||
}
|
||||
|
||||
@@ -131,10 +131,23 @@ type RecordingDepletionDTO struct {
|
||||
ProductWarehouse productWarehouseDTO.ProductWarehouseDTO `json:"product_warehouse"`
|
||||
}
|
||||
|
||||
type RecordingStockAllocDTO struct {
|
||||
SourceType string `json:"source_type"`
|
||||
SourceId uint `json:"source_id"`
|
||||
PrNumber string `json:"pr_number"`
|
||||
PoNumber string `json:"po_number"`
|
||||
AdjNumber string `json:"adj_number"`
|
||||
Qty float64 `json:"qty"`
|
||||
UnitPrice float64 `json:"unit_price"`
|
||||
Subtotal float64 `json:"subtotal"`
|
||||
}
|
||||
|
||||
type RecordingStockDTO struct {
|
||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||
UsageAmount float64 `json:"usage_amount"`
|
||||
PendingQty float64 `json:"pending_qty"`
|
||||
TotalPrice float64 `json:"total_price"`
|
||||
Allocations []RecordingStockAllocDTO `json:"allocations"`
|
||||
ProductWarehouse productWarehouseDTO.ProductWarehouseDTO `json:"product_warehouse"`
|
||||
}
|
||||
|
||||
@@ -197,10 +210,26 @@ func ToRecordingStockDTOs(stocks []entity.RecordingStock) []RecordingStockDTO {
|
||||
pendingQty = *s.PendingQty
|
||||
}
|
||||
|
||||
allocs := make([]RecordingStockAllocDTO, len(s.Allocations))
|
||||
for j, a := range s.Allocations {
|
||||
allocs[j] = RecordingStockAllocDTO{
|
||||
SourceType: a.SourceType,
|
||||
SourceId: a.SourceId,
|
||||
PrNumber: a.PrNumber,
|
||||
PoNumber: a.PoNumber,
|
||||
AdjNumber: a.AdjNumber,
|
||||
Qty: a.Qty,
|
||||
UnitPrice: a.UnitPrice,
|
||||
Subtotal: a.Subtotal,
|
||||
}
|
||||
}
|
||||
|
||||
result[i] = RecordingStockDTO{
|
||||
ProductWarehouseId: s.ProductWarehouseId,
|
||||
UsageAmount: usageAmount,
|
||||
PendingQty: pendingQty,
|
||||
TotalPrice: s.TotalPrice,
|
||||
Allocations: allocs,
|
||||
ProductWarehouse: mapProductWarehouseDTO(&s.ProductWarehouse),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ type RecordingRepository interface {
|
||||
ResyncProjectFlockPopulationUsage(ctx context.Context, tx *gorm.DB, projectFlockKandangID uint) error
|
||||
ValidateProductWarehousesByFlags(ctx context.Context, ids []uint, flags []string) (uint, error)
|
||||
GetProgressRows(ctx context.Context, startDate, endDate time.Time, allowedLocationIDs []uint, restrict bool) ([]exportprogress.Row, error)
|
||||
GetStockAllocationsByIDs(ctx context.Context, stockIDs []uint) (map[uint][]entity.RecordingStockAlloc, error)
|
||||
}
|
||||
|
||||
type RecordingRepositoryImpl struct {
|
||||
@@ -1231,3 +1232,71 @@ func (r *RecordingRepositoryImpl) GetTotalWeightProducedFromUniformityByProjectF
|
||||
|
||||
return result.TotalWeight, err
|
||||
}
|
||||
|
||||
func (r *RecordingRepositoryImpl) GetStockAllocationsByIDs(ctx context.Context, stockIDs []uint) (map[uint][]entity.RecordingStockAlloc, error) {
|
||||
if len(stockIDs) == 0 {
|
||||
return map[uint][]entity.RecordingStockAlloc{}, nil
|
||||
}
|
||||
|
||||
type row struct {
|
||||
RecordingStockId uint
|
||||
SourceType string
|
||||
SourceId uint
|
||||
PrNumber string
|
||||
PoNumber string
|
||||
AdjNumber string
|
||||
Qty float64
|
||||
UnitPrice float64
|
||||
Subtotal float64
|
||||
}
|
||||
|
||||
var rows []row
|
||||
err := r.DB().WithContext(ctx).Raw(`
|
||||
SELECT
|
||||
sa.usable_id AS recording_stock_id,
|
||||
sa.stockable_type AS source_type,
|
||||
sa.stockable_id AS source_id,
|
||||
COALESCE(p.pr_number, '') AS pr_number,
|
||||
COALESCE(p.po_number, '') AS po_number,
|
||||
COALESCE(ast.adj_number, '') AS adj_number,
|
||||
sa.qty AS qty,
|
||||
COALESCE(CASE
|
||||
WHEN sa.stockable_type = 'PURCHASE_ITEMS' THEN pi.price
|
||||
WHEN sa.stockable_type = 'ADJUSTMENT_IN' THEN ast.price
|
||||
END, 0) AS unit_price,
|
||||
sa.qty * COALESCE(CASE
|
||||
WHEN sa.stockable_type = 'PURCHASE_ITEMS' THEN pi.price
|
||||
WHEN sa.stockable_type = 'ADJUSTMENT_IN' THEN ast.price
|
||||
END, 0) AS subtotal
|
||||
FROM stock_allocations sa
|
||||
LEFT JOIN purchase_items pi
|
||||
ON pi.id = sa.stockable_id AND sa.stockable_type = 'PURCHASE_ITEMS'
|
||||
LEFT JOIN purchases p
|
||||
ON p.id = pi.purchase_id
|
||||
LEFT JOIN adjustment_stocks ast
|
||||
ON ast.id = sa.stockable_id AND sa.stockable_type = 'ADJUSTMENT_IN'
|
||||
WHERE sa.usable_type = 'RECORDING_STOCK'
|
||||
AND sa.usable_id IN ?
|
||||
AND sa.status = 'ACTIVE'
|
||||
AND sa.allocation_purpose = 'CONSUME'
|
||||
ORDER BY sa.usable_id, sa.id
|
||||
`, stockIDs).Scan(&rows).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := make(map[uint][]entity.RecordingStockAlloc)
|
||||
for _, row := range rows {
|
||||
result[row.RecordingStockId] = append(result[row.RecordingStockId], entity.RecordingStockAlloc{
|
||||
SourceType: row.SourceType,
|
||||
SourceId: row.SourceId,
|
||||
PrNumber: row.PrNumber,
|
||||
PoNumber: row.PoNumber,
|
||||
AdjNumber: row.AdjNumber,
|
||||
Qty: row.Qty,
|
||||
UnitPrice: row.UnitPrice,
|
||||
Subtotal: row.Subtotal,
|
||||
})
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
@@ -278,6 +278,26 @@ func (s recordingService) GetOne(c *fiber.Ctx, id uint) (*entity.Recording, erro
|
||||
s.Log.Errorf("Failed get recording by id: %+v", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(recording.Stocks) > 0 {
|
||||
stockIDs := make([]uint, len(recording.Stocks))
|
||||
for i, s := range recording.Stocks {
|
||||
stockIDs[i] = s.Id
|
||||
}
|
||||
if allocMap, err := s.Repository.GetStockAllocationsByIDs(c.Context(), stockIDs); err != nil {
|
||||
s.Log.Warnf("Failed to get stock allocations for recording %d: %+v", id, err)
|
||||
} else {
|
||||
for i := range recording.Stocks {
|
||||
allocs := allocMap[recording.Stocks[i].Id]
|
||||
recording.Stocks[i].Allocations = allocs
|
||||
var total float64
|
||||
for _, a := range allocs {
|
||||
total += a.Subtotal
|
||||
}
|
||||
recording.Stocks[i].TotalPrice = total
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if err := recordingutil.AttachLatestApproval(c.Context(), recording, s.ApprovalSvc, s.Log); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package dto
|
||||
|
||||
import (
|
||||
"math"
|
||||
"testing"
|
||||
|
||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||
)
|
||||
|
||||
func TestToMarketingReportItemsUsesDeliveryProductTotalWeight(t *testing.T) {
|
||||
mdps := []entity.MarketingDeliveryProduct{
|
||||
{
|
||||
Id: 1,
|
||||
UsageQty: 10,
|
||||
AvgWeight: 2.5,
|
||||
TotalWeight: 17.75,
|
||||
UnitPrice: 1000,
|
||||
},
|
||||
}
|
||||
|
||||
got := ToMarketingReportItems(mdps, nil, nil, nil)
|
||||
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("expected 1 marketing report item, got %d", len(got))
|
||||
}
|
||||
if got[0].TotalWeightKg != 17.75 {
|
||||
t.Fatalf("expected total_weight_kg to use delivery product total_weight 17.75, got %.2f", got[0].TotalWeightKg)
|
||||
}
|
||||
if got[0].Qty != 10 {
|
||||
t.Fatalf("expected qty to stay from usage_qty, got %.2f", got[0].Qty)
|
||||
}
|
||||
if got[0].AverageWeightKg != 2.5 {
|
||||
t.Fatalf("expected average_weight_kg to stay from avg_weight, got %.2f", got[0].AverageWeightKg)
|
||||
}
|
||||
if got[0].SalesAmount != 17750 {
|
||||
t.Fatalf("expected sales_amount to use delivery product total_weight, got %.2f", got[0].SalesAmount)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarketingSummaryUsesReportItemTotalWeight(t *testing.T) {
|
||||
items := []RepportMarketingItemDTO{
|
||||
{
|
||||
Qty: 10,
|
||||
TotalWeightKg: 17.75,
|
||||
SalesAmount: 17750,
|
||||
},
|
||||
{
|
||||
Qty: 5,
|
||||
TotalWeightKg: 8.25,
|
||||
SalesAmount: 8250,
|
||||
},
|
||||
}
|
||||
|
||||
got := ToSummaryFromDTOItems(items)
|
||||
|
||||
if got == nil {
|
||||
t.Fatal("expected summary, got nil")
|
||||
}
|
||||
if got.TotalWeightKg != 26 {
|
||||
t.Fatalf("expected summary total_weight_kg to sum item total weights, got %.2f", got.TotalWeightKg)
|
||||
}
|
||||
if diff := math.Abs(got.AverageWeightKg - (26.0 / 15.0)); diff > 0.000001 {
|
||||
t.Fatalf("expected summary average_weight_kg to use total_weight_kg / total_qty, got %.6f", got.AverageWeightKg)
|
||||
}
|
||||
if got.TotalQty != 15 {
|
||||
t.Fatalf("expected total qty 15, got %d", got.TotalQty)
|
||||
}
|
||||
if got.TotalSalesAmount != 26000 {
|
||||
t.Fatalf("expected total sales amount 26000, got %d", got.TotalSalesAmount)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user