mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 05:21:57 +00:00
493 lines
16 KiB
Go
493 lines
16 KiB
Go
package repositories
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
|
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations"
|
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
|
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type DebtSupplierRepository interface {
|
|
GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.DebtSupplierQuery) ([]entity.Supplier, int64, error)
|
|
GetPurchasesBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) ([]entity.Purchase, error)
|
|
GetPaymentsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) ([]entity.Payment, error)
|
|
GetPaymentTotalsByReferences(ctx context.Context, supplierIDs []uint, references []string) (map[string]float64, error)
|
|
GetPaymentSummariesByReferences(ctx context.Context, supplierIDs []uint, references []string) (map[string]PaymentReferenceSummary, error)
|
|
GetInitialBalanceTotals(ctx context.Context, supplierIDs []uint) (map[uint]float64, error)
|
|
GetPurchaseTotalsBeforeDate(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) (map[uint]float64, error)
|
|
GetPaymentTotalsBeforeDate(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) (map[uint]float64, error)
|
|
}
|
|
|
|
type debtSupplierRepositoryImpl struct {
|
|
db *gorm.DB
|
|
}
|
|
|
|
type PaymentReferenceSummary struct {
|
|
Total float64
|
|
LatestPaymentDate time.Time
|
|
}
|
|
|
|
func NewDebtSupplierRepository(db *gorm.DB) DebtSupplierRepository {
|
|
return &debtSupplierRepositoryImpl{db: db}
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) latestPurchaseApproval(ctx context.Context) *gorm.DB {
|
|
return r.db.WithContext(ctx).
|
|
Table("approvals AS a").
|
|
Select("a.approvable_id, a.step_number, a.action").
|
|
Joins(`
|
|
JOIN (
|
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
|
FROM approvals
|
|
WHERE approvable_type = ?
|
|
GROUP BY approvable_id
|
|
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
|
string(utils.ApprovalWorkflowPurchase),
|
|
)
|
|
}
|
|
|
|
func resolveDebtSupplierSortClause(filters *validation.DebtSupplierQuery) string {
|
|
direction := "ASC"
|
|
if strings.EqualFold(strings.TrimSpace(filters.SortOrder), "desc") {
|
|
direction = "DESC"
|
|
}
|
|
switch strings.ToLower(strings.TrimSpace(filters.SortBy)) {
|
|
case "supplier":
|
|
return "suppliers.name " + direction
|
|
default:
|
|
return "suppliers.name ASC"
|
|
}
|
|
}
|
|
|
|
func resolveDebtSupplierDateColumn(filterBy string) string {
|
|
switch strings.ToLower(strings.TrimSpace(filterBy)) {
|
|
case "po_date":
|
|
return "purchases.po_date"
|
|
case "received_date", "":
|
|
return "purchase_items.received_date"
|
|
default:
|
|
return "purchase_items.received_date"
|
|
}
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filters *validation.DebtSupplierQuery) *gorm.DB {
|
|
dateColumn := resolveDebtSupplierDateColumn(filters.FilterBy)
|
|
|
|
db := r.db.WithContext(ctx).
|
|
Model(&entity.Supplier{}).
|
|
Joins("JOIN purchases ON purchases.supplier_id = suppliers.id").
|
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
|
Joins("JOIN warehouses w ON w.id = purchase_items.warehouse_id").
|
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
|
Where("purchase_items.received_date IS NOT NULL")
|
|
|
|
if len(filters.SupplierIDs) > 0 {
|
|
db = db.Where("suppliers.id IN ?", filters.SupplierIDs)
|
|
}
|
|
|
|
if filters.AllowedAreaIDs != nil {
|
|
if len(filters.AllowedAreaIDs) == 0 {
|
|
db = db.Where("1 = 0")
|
|
} else {
|
|
db = db.Where("w.area_id IN ?", filters.AllowedAreaIDs)
|
|
}
|
|
}
|
|
|
|
if filters.AllowedLocationIDs != nil {
|
|
if len(filters.AllowedLocationIDs) == 0 {
|
|
db = db.Where("1 = 0")
|
|
} else {
|
|
db = db.Where("w.location_id IN ?", filters.AllowedLocationIDs)
|
|
}
|
|
}
|
|
|
|
if filters.StartDate != "" {
|
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
|
db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
|
|
}
|
|
}
|
|
|
|
if filters.EndDate != "" {
|
|
if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
|
|
db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo)
|
|
}
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.DebtSupplierQuery) ([]entity.Supplier, int64, error) {
|
|
query := r.baseSupplierQuery(ctx, filters)
|
|
|
|
var totalSuppliers int64
|
|
if err := query.
|
|
Distinct("suppliers.id").
|
|
Count(&totalSuppliers).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
if totalSuppliers == 0 {
|
|
return []entity.Supplier{}, 0, nil
|
|
}
|
|
|
|
if offset < 0 {
|
|
offset = 0
|
|
}
|
|
|
|
type supplierIDResult struct {
|
|
ID uint `gorm:"column:id"`
|
|
Name string `gorm:"column:name"`
|
|
}
|
|
var idResults []supplierIDResult
|
|
if err := r.baseSupplierQuery(ctx, filters).
|
|
Select("suppliers.id, suppliers.name").
|
|
Group("suppliers.id, suppliers.name").
|
|
Order(resolveDebtSupplierSortClause(filters)).
|
|
Offset(offset).
|
|
Limit(limit).
|
|
Scan(&idResults).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
supplierIDs := make([]uint, 0, len(idResults))
|
|
for _, r := range idResults {
|
|
supplierIDs = append(supplierIDs, r.ID)
|
|
}
|
|
|
|
if len(supplierIDs) == 0 {
|
|
return []entity.Supplier{}, totalSuppliers, nil
|
|
}
|
|
|
|
var suppliers []entity.Supplier
|
|
if err := r.db.WithContext(ctx).
|
|
Where("id IN ?", supplierIDs).
|
|
Order(resolveDebtSupplierSortClause(filters)).
|
|
Find(&suppliers).Error; err != nil {
|
|
return nil, 0, err
|
|
}
|
|
|
|
return suppliers, totalSuppliers, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPurchasesBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) ([]entity.Purchase, error) {
|
|
if len(supplierIDs) == 0 {
|
|
return []entity.Purchase{}, nil
|
|
}
|
|
|
|
purchaseIDs, err := r.getPurchaseIDs(ctx, supplierIDs, filters)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(purchaseIDs) == 0 {
|
|
return []entity.Purchase{}, nil
|
|
}
|
|
|
|
preloadItems := func(db *gorm.DB) *gorm.DB {
|
|
db = db.
|
|
Preload("Warehouse").
|
|
Preload("Warehouse.Area").
|
|
Order("purchase_items.id ASC")
|
|
|
|
if strings.EqualFold(strings.TrimSpace(filters.FilterBy), "received_date") || strings.TrimSpace(filters.FilterBy) == "" {
|
|
if filters.StartDate != "" {
|
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
|
db = db.Where("DATE(purchase_items.received_date) >= ?", dateFrom)
|
|
}
|
|
}
|
|
if filters.EndDate != "" {
|
|
if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
|
|
db = db.Where("DATE(purchase_items.received_date) <= ?", dateTo)
|
|
}
|
|
}
|
|
}
|
|
|
|
return db
|
|
}
|
|
|
|
var purchases []entity.Purchase
|
|
if err := r.db.WithContext(ctx).
|
|
Model(&entity.Purchase{}).
|
|
Preload("Supplier").
|
|
Preload("Items", preloadItems).
|
|
Where("purchases.id IN ?", purchaseIDs).
|
|
Order("purchases.id ASC").
|
|
Find(&purchases).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return purchases, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPaymentsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) ([]entity.Payment, error) {
|
|
if len(supplierIDs) == 0 {
|
|
return []entity.Payment{}, nil
|
|
}
|
|
|
|
db := r.db.WithContext(ctx).
|
|
Model(&entity.Payment{}).
|
|
Where("party_type = ?", string(utils.PaymentPartySupplier)).
|
|
Where("direction = ?", "OUT").
|
|
Where("party_id IN ?", supplierIDs).
|
|
Where("transaction_type <> ?", string(utils.TransactionTypeSaldoAwal))
|
|
|
|
if strings.TrimSpace(filters.StartDate) != "" {
|
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
|
db = db.Where("DATE(payment_date) >= ?", dateFrom)
|
|
}
|
|
}
|
|
|
|
if strings.TrimSpace(filters.EndDate) != "" {
|
|
if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
|
|
db = db.Where("DATE(payment_date) <= ?", dateTo)
|
|
}
|
|
}
|
|
|
|
var payments []entity.Payment
|
|
if err := db.
|
|
Order("payment_date ASC, id ASC").
|
|
Find(&payments).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return payments, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) getPurchaseIDs(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) ([]uint, error) {
|
|
dateColumn := resolveDebtSupplierDateColumn(filters.FilterBy)
|
|
|
|
db := r.db.WithContext(ctx).
|
|
Table("purchases").
|
|
Select("DISTINCT purchases.id").
|
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
|
Joins("JOIN warehouses w ON w.id = purchase_items.warehouse_id").
|
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
|
Where("purchases.supplier_id IN ?", supplierIDs).
|
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
|
Where("purchase_items.received_date IS NOT NULL")
|
|
|
|
if filters.AllowedAreaIDs != nil {
|
|
if len(filters.AllowedAreaIDs) == 0 {
|
|
db = db.Where("1 = 0")
|
|
} else {
|
|
db = db.Where("w.area_id IN ?", filters.AllowedAreaIDs)
|
|
}
|
|
}
|
|
|
|
if filters.AllowedLocationIDs != nil {
|
|
if len(filters.AllowedLocationIDs) == 0 {
|
|
db = db.Where("1 = 0")
|
|
} else {
|
|
db = db.Where("w.location_id IN ?", filters.AllowedLocationIDs)
|
|
}
|
|
}
|
|
|
|
if filters.StartDate != "" {
|
|
if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil {
|
|
db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom)
|
|
}
|
|
}
|
|
|
|
if filters.EndDate != "" {
|
|
if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil {
|
|
db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo)
|
|
}
|
|
}
|
|
|
|
var purchaseIDs []uint
|
|
if err := db.Order("purchases.id ASC").Pluck("purchases.id", &purchaseIDs).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return purchaseIDs, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPaymentTotalsByReferences(ctx context.Context, supplierIDs []uint, references []string) (map[string]float64, error) {
|
|
if len(supplierIDs) == 0 || len(references) == 0 {
|
|
return map[string]float64{}, nil
|
|
}
|
|
|
|
type paymentRow struct {
|
|
ReferenceNumber *string `gorm:"column:reference_number"`
|
|
Total float64 `gorm:"column:total"`
|
|
}
|
|
|
|
rows := make([]paymentRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Model(&entity.Payment{}).
|
|
Select("reference_number, SUM(nominal) AS total").
|
|
Where("party_type = ?", string(utils.PaymentPartySupplier)).
|
|
Where("direction = ?", "OUT").
|
|
Where("party_id IN ?", supplierIDs).
|
|
Where("reference_number IN ?", references).
|
|
Where("transaction_type <> ?", string(utils.TransactionTypeSaldoAwal)).
|
|
Group("reference_number").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]float64, len(rows))
|
|
for _, row := range rows {
|
|
if row.ReferenceNumber == nil || strings.TrimSpace(*row.ReferenceNumber) == "" {
|
|
continue
|
|
}
|
|
result[*row.ReferenceNumber] = row.Total
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPaymentSummariesByReferences(ctx context.Context, supplierIDs []uint, references []string) (map[string]PaymentReferenceSummary, error) {
|
|
if len(supplierIDs) == 0 || len(references) == 0 {
|
|
return map[string]PaymentReferenceSummary{}, nil
|
|
}
|
|
|
|
type paymentRow struct {
|
|
ReferenceNumber *string `gorm:"column:reference_number"`
|
|
Total float64 `gorm:"column:total"`
|
|
LatestPaymentDate time.Time `gorm:"column:latest_payment_date"`
|
|
}
|
|
|
|
rows := make([]paymentRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Model(&entity.Payment{}).
|
|
Select("reference_number, SUM(nominal) AS total, MAX(payment_date) AS latest_payment_date").
|
|
Where("party_type = ?", string(utils.PaymentPartySupplier)).
|
|
Where("direction = ?", "OUT").
|
|
Where("party_id IN ?", supplierIDs).
|
|
Where("reference_number IN ?", references).
|
|
Where("transaction_type <> ?", string(utils.TransactionTypeSaldoAwal)).
|
|
Group("reference_number").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[string]PaymentReferenceSummary, len(rows))
|
|
for _, row := range rows {
|
|
if row.ReferenceNumber == nil || strings.TrimSpace(*row.ReferenceNumber) == "" {
|
|
continue
|
|
}
|
|
result[*row.ReferenceNumber] = PaymentReferenceSummary{
|
|
Total: row.Total,
|
|
LatestPaymentDate: row.LatestPaymentDate,
|
|
}
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetInitialBalanceTotals(ctx context.Context, supplierIDs []uint) (map[uint]float64, error) {
|
|
if len(supplierIDs) == 0 {
|
|
return map[uint]float64{}, nil
|
|
}
|
|
|
|
type balanceRow struct {
|
|
SupplierID uint `gorm:"column:supplier_id"`
|
|
Total float64 `gorm:"column:total"`
|
|
}
|
|
|
|
rows := make([]balanceRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Model(&entity.Payment{}).
|
|
Select("party_id AS supplier_id, SUM(nominal) AS total").
|
|
Where("party_type = ?", string(utils.PaymentPartySupplier)).
|
|
Where("party_id IN ?", supplierIDs).
|
|
Where("transaction_type = ?", string(utils.TransactionTypeSaldoAwal)).
|
|
Group("party_id").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[uint]float64, len(rows))
|
|
for _, row := range rows {
|
|
result[row.SupplierID] = row.Total
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPurchaseTotalsBeforeDate(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) (map[uint]float64, error) {
|
|
if len(supplierIDs) == 0 || strings.TrimSpace(filters.StartDate) == "" {
|
|
return map[uint]float64{}, nil
|
|
}
|
|
|
|
dateFrom, err := utils.ParseDateString(filters.StartDate)
|
|
if err != nil {
|
|
return map[uint]float64{}, nil
|
|
}
|
|
|
|
dateColumn := resolveDebtSupplierDateColumn(filters.FilterBy)
|
|
|
|
type purchaseTotalRow struct {
|
|
SupplierID uint `gorm:"column:supplier_id"`
|
|
Total float64 `gorm:"column:total"`
|
|
}
|
|
|
|
rows := make([]purchaseTotalRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Table("purchases").
|
|
Select("purchases.supplier_id AS supplier_id, SUM(purchase_items.total_price) AS total").
|
|
Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id").
|
|
Joins("JOIN (?) AS la ON la.approvable_id = purchases.id", r.latestPurchaseApproval(ctx)).
|
|
Where("purchases.supplier_id IN ?", supplierIDs).
|
|
Where("la.step_number >= ?", uint16(utils.PurchaseStepReceiving)).
|
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
|
Where("purchase_items.received_date IS NOT NULL").
|
|
Where(fmt.Sprintf("DATE(%s) < ?", dateColumn), dateFrom).
|
|
Group("purchases.supplier_id").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[uint]float64, len(rows))
|
|
for _, row := range rows {
|
|
result[row.SupplierID] = row.Total
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (r *debtSupplierRepositoryImpl) GetPaymentTotalsBeforeDate(ctx context.Context, supplierIDs []uint, filters *validation.DebtSupplierQuery) (map[uint]float64, error) {
|
|
if len(supplierIDs) == 0 || strings.TrimSpace(filters.StartDate) == "" {
|
|
return map[uint]float64{}, nil
|
|
}
|
|
|
|
dateFrom, err := utils.ParseDateString(filters.StartDate)
|
|
if err != nil {
|
|
return map[uint]float64{}, nil
|
|
}
|
|
|
|
type paymentTotalRow struct {
|
|
SupplierID uint `gorm:"column:supplier_id"`
|
|
Total float64 `gorm:"column:total"`
|
|
}
|
|
|
|
rows := make([]paymentTotalRow, 0)
|
|
if err := r.db.WithContext(ctx).
|
|
Model(&entity.Payment{}).
|
|
Select("party_id AS supplier_id, SUM(nominal) AS total").
|
|
Where("party_type = ?", string(utils.PaymentPartySupplier)).
|
|
Where("direction = ?", "OUT").
|
|
Where("party_id IN ?", supplierIDs).
|
|
Where("transaction_type <> ?", string(utils.TransactionTypeSaldoAwal)).
|
|
Where("DATE(payment_date) < ?", dateFrom).
|
|
Group("party_id").
|
|
Scan(&rows).Error; err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
result := make(map[uint]float64, len(rows))
|
|
for _, row := range rows {
|
|
result[row.SupplierID] = row.Total
|
|
}
|
|
|
|
return result, nil
|
|
}
|