Files
lti-api/internal/modules/closings/repositories/closing.repository.go
T

431 lines
13 KiB
Go

package repository
import (
"context"
"fmt"
"time"
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations"
"gitlab.com/mbugroup/lti-api.git/internal/utils"
"gorm.io/gorm"
)
type ClosingRepository interface {
repository.BaseRepository[entity.ProjectFlock]
ListProjectFlockKandangsForSapronak(ctx context.Context, params *validation.SapronakQuery) ([]entity.ProjectFlockKandang, error)
MapSapronakStartDates(ctx context.Context, pfkIDs []uint) (map[uint]time.Time, error)
FetchSapronakIncoming(ctx context.Context, kandangID uint, start, end *time.Time) ([]SapronakIncomingRow, error)
FetchSapronakIncomingDetails(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
FetchSapronakUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error)
FetchSapronakUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error)
FetchSapronakAdjustments(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
FetchSapronakTransfers(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error)
}
type ClosingRepositoryImpl struct {
*repository.BaseRepositoryImpl[entity.ProjectFlock]
}
func NewClosingRepository(db *gorm.DB) ClosingRepository {
return &ClosingRepositoryImpl{
BaseRepositoryImpl: repository.NewBaseRepository[entity.ProjectFlock](db),
}
}
type SapronakIncomingRow struct {
ProductID uint
ProductName string
Flag string
Qty float64
Value float64
DefaultPrice float64
}
type SapronakUsageRow struct {
ProductID uint
ProductName string
Flag string
Qty float64
DefaultPrice float64
}
type SapronakDetailRow struct {
ProductID uint
ProductName string
Flag string
Date *time.Time
Reference string
QtyIn float64
QtyOut float64
Price float64
}
func (r *ClosingRepositoryImpl) ListProjectFlockKandangsForSapronak(ctx context.Context, params *validation.SapronakQuery) ([]entity.ProjectFlockKandang, error) {
db := r.DB().
WithContext(ctx).
Preload("ProjectFlock").
Preload("Kandang")
if params != nil {
if params.ProjectFlockID > 0 {
db = db.Where("project_flock_kandangs.project_flock_id = ?", params.ProjectFlockID)
}
if params.KandangID > 0 {
db = db.Where("project_flock_kandangs.kandang_id = ?", params.KandangID)
}
if params.ProjectFlockKandangID > 0 {
db = db.Where("project_flock_kandangs.id = ?", params.ProjectFlockKandangID)
}
}
var pfks []entity.ProjectFlockKandang
if err := db.Find(&pfks).Error; err != nil {
return nil, err
}
return pfks, nil
}
func (r *ClosingRepositoryImpl) MapSapronakStartDates(ctx context.Context, pfkIDs []uint) (map[uint]time.Time, error) {
result := make(map[uint]time.Time, len(pfkIDs))
if len(pfkIDs) == 0 {
return result, nil
}
var rows []struct {
ProjectFlockKandangID uint `gorm:"column:project_flock_kandang_id"`
StartDate *time.Time `gorm:"column:start_date"`
}
if err := r.DB().
WithContext(ctx).
Table("project_chickins").
Select("project_flock_kandang_id, MIN(chick_in_date) AS start_date").
Where("project_flock_kandang_id IN ?", pfkIDs).
Group("project_flock_kandang_id").
Scan(&rows).Error; err != nil {
return nil, err
}
for _, row := range rows {
if row.StartDate != nil {
result[row.ProjectFlockKandangID] = row.StartDate.UTC()
}
}
return result, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakIncoming(ctx context.Context, kandangID uint, start, end *time.Time) ([]SapronakIncomingRow, error) {
rows := make([]SapronakIncomingRow, 0)
db := r.DB().
WithContext(ctx).
Table("purchase_items AS pi").
Select(`
pi.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
COALESCE(SUM(pi.total_qty), 0) AS qty,
COALESCE(SUM(pi.total_qty * pi.price), 0) AS value,
COALESCE(p.product_price, 0) AS default_price
`).
Joins("JOIN purchases po ON po.id = pi.purchase_id AND po.deleted_at IS NULL").
Joins("JOIN products p ON p.id = pi.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Joins("JOIN warehouses w ON w.id = pi.warehouse_id").
Where("w.kandang_id = ?", kandangID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)}).
Where("pi.received_date IS NOT NULL")
if start != nil {
db = db.Where("pi.received_date >= ?", *start)
}
if end != nil {
db = db.Where("pi.received_date < ?", *end)
}
if err := db.Group("pi.product_id, p.name, f.name, p.product_price").Scan(&rows).Error; err != nil {
return nil, err
}
return rows, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakUsage(ctx context.Context, pfkID uint, start, end *time.Time) ([]SapronakUsageRow, error) {
rows := make([]SapronakUsageRow, 0)
if pfkID == 0 {
return rows, nil
}
db := r.DB().
WithContext(ctx).
Table("recording_stocks AS rs").
Select(`
pw.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
COALESCE(SUM(rs.usage_qty), 0) AS qty,
COALESCE(p.product_price, 0) AS default_price
`).
Joins("JOIN recordings r ON r.id = rs.recording_id AND r.deleted_at IS NULL").
Joins("JOIN product_warehouses pw ON pw.id = rs.product_warehouse_id").
Joins("JOIN products p ON p.id = pw.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Where("r.project_flock_kandangs_id = ?", pfkID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)})
if start != nil {
db = db.Where("r.record_datetime >= ?", *start)
}
if end != nil {
db = db.Where("r.record_datetime < ?", *end)
}
if err := db.Group("pw.product_id, p.name, f.name, p.product_price").Scan(&rows).Error; err != nil {
return nil, err
}
return rows, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakIncomingDetails(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
rows := make([]SapronakDetailRow, 0)
db := r.DB().
WithContext(ctx).
Table("purchase_items AS pi").
Select(`
pi.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
pi.received_date AS date,
COALESCE(po.po_number, '') AS reference,
COALESCE(pi.total_qty,0) AS qty_in,
0 AS qty_out,
COALESCE(pi.price,0) AS price
`).
Joins("JOIN purchases po ON po.id = pi.purchase_id AND po.deleted_at IS NULL").
Joins("JOIN products p ON p.id = pi.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Joins("JOIN warehouses w ON w.id = pi.warehouse_id").
Where("w.kandang_id = ?", kandangID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)}).
Where("pi.received_date IS NOT NULL")
if start != nil {
db = db.Where("pi.received_date >= ?", *start)
}
if end != nil {
db = db.Where("pi.received_date < ?", *end)
}
if err := db.Scan(&rows).Error; err != nil {
return nil, err
}
result := make(map[uint][]SapronakDetailRow)
for _, row := range rows {
result[row.ProductID] = append(result[row.ProductID], row)
}
return result, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakUsageDetails(ctx context.Context, pfkID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, error) {
rows := make([]SapronakDetailRow, 0)
db := r.DB().
WithContext(ctx).
Table("recording_stocks AS rs").
Select(`
pw.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
r.record_datetime AS date,
CAST(r.id AS TEXT) AS reference,
0 AS qty_in,
COALESCE(rs.usage_qty,0) AS qty_out,
COALESCE(p.product_price,0) AS price
`).
Joins("JOIN recordings r ON r.id = rs.recording_id AND r.deleted_at IS NULL").
Joins("JOIN product_warehouses pw ON pw.id = rs.product_warehouse_id").
Joins("JOIN products p ON p.id = pw.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Where("r.project_flock_kandangs_id = ?", pfkID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)})
if start != nil {
db = db.Where("r.record_datetime >= ?", *start)
}
if end != nil {
db = db.Where("r.record_datetime < ?", *end)
}
if err := db.Scan(&rows).Error; err != nil {
return nil, err
}
result := make(map[uint][]SapronakDetailRow)
for _, row := range rows {
result[row.ProductID] = append(result[row.ProductID], row)
}
return result, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakAdjustments(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error) {
incoming := make(map[uint][]SapronakDetailRow)
outgoing := make(map[uint][]SapronakDetailRow)
rows := make([]struct {
ID uint
ProductID uint
ProductName string
Flag string
CreatedAt *time.Time
Increase float64
Decrease float64
Price float64
}, 0)
db := r.DB().
WithContext(ctx).
Table("stock_logs sl").
Select(`
sl.id AS id,
pw.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
sl.created_at AS created_at,
COALESCE(sl.increase,0) AS increase,
COALESCE(sl.decrease,0) AS decrease,
COALESCE(p.product_price,0) AS price
`).
Joins("JOIN product_warehouses pw ON pw.id = sl.product_warehouse_id").
Joins("JOIN products p ON p.id = pw.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
Where("sl.loggable_type = ?", entity.LogTypeAdjustment).
Where("w.kandang_id = ?", kandangID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)})
if start != nil {
db = db.Where("sl.created_at >= ?", *start)
}
if end != nil {
db = db.Where("sl.created_at < ?", *end)
}
if err := db.Scan(&rows).Error; err != nil {
return nil, nil, err
}
for _, row := range rows {
ref := fmt.Sprintf("ADJ-%d", row.ID)
if row.Increase > 0 {
incoming[row.ProductID] = append(incoming[row.ProductID], SapronakDetailRow{
ProductID: row.ProductID,
ProductName: row.ProductName,
Flag: row.Flag,
Date: row.CreatedAt,
Reference: ref,
QtyIn: row.Increase,
QtyOut: 0,
Price: row.Price,
})
}
if row.Decrease > 0 {
outgoing[row.ProductID] = append(outgoing[row.ProductID], SapronakDetailRow{
ProductID: row.ProductID,
ProductName: row.ProductName,
Flag: row.Flag,
Date: row.CreatedAt,
Reference: ref,
QtyIn: 0,
QtyOut: row.Decrease,
Price: row.Price,
})
}
}
return incoming, outgoing, nil
}
func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kandangID uint, start, end *time.Time) (map[uint][]SapronakDetailRow, map[uint][]SapronakDetailRow, error) {
incoming := make(map[uint][]SapronakDetailRow)
outgoing := make(map[uint][]SapronakDetailRow)
rows := make([]struct {
ID uint
ProductID uint
ProductName string
Flag string
CreatedAt *time.Time
Increase float64
Decrease float64
Price float64
}, 0)
db := r.DB().
WithContext(ctx).
Table("stock_logs sl").
Select(`
sl.id AS id,
pw.product_id AS product_id,
p.name AS product_name,
f.name AS flag,
sl.created_at AS created_at,
COALESCE(sl.increase,0) AS increase,
COALESCE(sl.decrease,0) AS decrease,
COALESCE(p.product_price,0) AS price
`).
Joins("JOIN product_warehouses pw ON pw.id = sl.product_warehouse_id").
Joins("JOIN products p ON p.id = pw.product_id").
Joins("JOIN flags f ON f.flagable_id = p.id AND f.flagable_type = ?", entity.FlagableTypeProduct).
Joins("JOIN warehouses w ON w.id = pw.warehouse_id").
Where("sl.loggable_type = ?", entity.LogTypeTransfer).
Where("w.kandang_id = ?", kandangID).
Where("f.name IN ?", []string{string(utils.FlagDOC), string(utils.FlagPakan), string(utils.FlagOVK)})
if start != nil {
db = db.Where("sl.created_at >= ?", *start)
}
if end != nil {
db = db.Where("sl.created_at < ?", *end)
}
if err := db.Scan(&rows).Error; err != nil {
return nil, nil, err
}
for _, row := range rows {
ref := fmt.Sprintf("TRF-%d", row.ID)
if row.Increase > 0 {
incoming[row.ProductID] = append(incoming[row.ProductID], SapronakDetailRow{
ProductID: row.ProductID,
ProductName: row.ProductName,
Flag: row.Flag,
Date: row.CreatedAt,
Reference: ref,
QtyIn: row.Increase,
QtyOut: 0,
Price: row.Price,
})
}
if row.Decrease > 0 {
outgoing[row.ProductID] = append(outgoing[row.ProductID], SapronakDetailRow{
ProductID: row.ProductID,
ProductName: row.ProductName,
Flag: row.Flag,
Date: row.CreatedAt,
Reference: ref,
QtyIn: 0,
QtyOut: row.Decrease,
Price: row.Price,
})
}
}
return incoming, outgoing, nil
}