mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-23 23:05:44 +00:00
Merge branch 'development' into 'staging'
Development See merge request mbugroup/lti-api!310
This commit is contained in:
@@ -743,8 +743,6 @@ func (s *fifoService) releaseUsagePortion(
|
|||||||
if expectedWarehouseID == 0 || alloc.ProductWarehouseId == expectedWarehouseID {
|
if expectedWarehouseID == 0 || alloc.ProductWarehouseId == expectedWarehouseID {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fmt.Printf("WARN[FIFO] ALLOC WAREHOUSE MISMATCH usable_key=%s usable_id=%d alloc_id=%d expected_pw=%d actual_pw=%d\n",
|
|
||||||
usableKey.String(), usableID, alloc.Id, expectedWarehouseID, alloc.ProductWarehouseId)
|
|
||||||
if err := tx.Model(&entities.StockAllocation{}).
|
if err := tx.Model(&entities.StockAllocation{}).
|
||||||
Where("id = ?", alloc.Id).
|
Where("id = ?", alloc.Id).
|
||||||
Update("product_warehouse_id", expectedWarehouseID).Error; err != nil {
|
Update("product_warehouse_id", expectedWarehouseID).Error; err != nil {
|
||||||
@@ -848,41 +846,80 @@ func (s *fifoService) fetchPendingCandidates(ctx context.Context, tx *gorm.DB, p
|
|||||||
cfg.Columns.CreatedAt,
|
cfg.Columns.CreatedAt,
|
||||||
)
|
)
|
||||||
|
|
||||||
var rows []struct {
|
if cfg.Columns.CreatedAt == cfg.Columns.ID {
|
||||||
ID uint
|
var rows []struct {
|
||||||
Pending float64
|
ID uint
|
||||||
CreatedAt time.Time
|
Pending float64
|
||||||
}
|
CreatedAt int64
|
||||||
|
}
|
||||||
query := tx.Table(cfg.Table).
|
|
||||||
Select(selectStmt).
|
query := tx.Table(cfg.Table).
|
||||||
Where(fmt.Sprintf("%s = ?", cfg.Columns.ProductWarehouseID), productWarehouseID).
|
Select(selectStmt).
|
||||||
Where(fmt.Sprintf("%s > 0", cfg.Columns.PendingQuantity)).
|
Where(fmt.Sprintf("%s = ?", cfg.Columns.ProductWarehouseID), productWarehouseID).
|
||||||
Limit(s.pendingBatchPerUsable)
|
Where(fmt.Sprintf("%s > 0", cfg.Columns.PendingQuantity)).
|
||||||
|
Limit(s.pendingBatchPerUsable)
|
||||||
if cfg.Scope != nil {
|
|
||||||
query = cfg.Scope(query)
|
if cfg.Scope != nil {
|
||||||
}
|
query = cfg.Scope(query)
|
||||||
|
}
|
||||||
for _, order := range s.orderClauses(cfg.OrderBy) {
|
|
||||||
query = query.Order(order)
|
for _, order := range s.orderClauses(cfg.OrderBy) {
|
||||||
}
|
query = query.Order(order)
|
||||||
|
}
|
||||||
if err := query.Find(&rows).Error; err != nil {
|
|
||||||
return nil, err
|
if err := query.Find(&rows).Error; err != nil {
|
||||||
}
|
return nil, err
|
||||||
|
}
|
||||||
for _, row := range rows {
|
|
||||||
if row.Pending <= 0 {
|
for _, row := range rows {
|
||||||
continue
|
if row.Pending <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidates = append(candidates, pendingCandidate{
|
||||||
|
UsableKey: key,
|
||||||
|
Config: cfg,
|
||||||
|
UsableID: row.ID,
|
||||||
|
Pending: row.Pending,
|
||||||
|
CreatedAt: time.Unix(0, row.CreatedAt),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var rows []struct {
|
||||||
|
ID uint
|
||||||
|
Pending float64
|
||||||
|
CreatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
query := tx.Table(cfg.Table).
|
||||||
|
Select(selectStmt).
|
||||||
|
Where(fmt.Sprintf("%s = ?", cfg.Columns.ProductWarehouseID), productWarehouseID).
|
||||||
|
Where(fmt.Sprintf("%s > 0", cfg.Columns.PendingQuantity)).
|
||||||
|
Limit(s.pendingBatchPerUsable)
|
||||||
|
|
||||||
|
if cfg.Scope != nil {
|
||||||
|
query = cfg.Scope(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, order := range s.orderClauses(cfg.OrderBy) {
|
||||||
|
query = query.Order(order)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := query.Find(&rows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
if row.Pending <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
candidates = append(candidates, pendingCandidate{
|
||||||
|
UsableKey: key,
|
||||||
|
Config: cfg,
|
||||||
|
UsableID: row.ID,
|
||||||
|
Pending: row.Pending,
|
||||||
|
CreatedAt: row.CreatedAt,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
candidates = append(candidates, pendingCandidate{
|
|
||||||
UsableKey: key,
|
|
||||||
Config: cfg,
|
|
||||||
UsableID: row.ID,
|
|
||||||
Pending: row.Pending,
|
|
||||||
CreatedAt: row.CreatedAt,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
-- Create sequence for transfer laying movement number
|
-- Create sequence for transfer laying movement number
|
||||||
CREATE SEQUENCE transfer_laying_seq START
|
CREATE SEQUENCE IF NOT EXISTS transfer_laying_seq START
|
||||||
WITH
|
WITH
|
||||||
1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 99999 NO CYCLE;
|
1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 99999 NO CYCLE;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE adjustment_stocks
|
||||||
|
DROP COLUMN adj_number;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE adjustment_stocks
|
||||||
|
ADD COLUMN adj_number VARCHAR(255);
|
||||||
|
|
||||||
|
UPDATE adjustment_stocks
|
||||||
|
SET adj_number = CONCAT('ADJ-', LPAD(id::text, 5, '0'))
|
||||||
|
WHERE adj_number IS NULL;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
-- Remove columns from marketing_products
|
||||||
|
ALTER TABLE marketing_products
|
||||||
|
DROP COLUMN IF EXISTS week,
|
||||||
|
DROP COLUMN IF EXISTS weight_per_convertion,
|
||||||
|
DROP COLUMN IF EXISTS convertion_unit;
|
||||||
|
|
||||||
|
-- Remove column from marketings
|
||||||
|
ALTER TABLE marketings DROP COLUMN IF EXISTS marketing_type;
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
-- Add marketing_type to marketings table
|
||||||
|
ALTER TABLE marketings
|
||||||
|
ADD COLUMN IF NOT EXISTS marketing_type VARCHAR(50);
|
||||||
|
|
||||||
|
-- Add convertion fields to marketing_products table
|
||||||
|
ALTER TABLE marketing_products
|
||||||
|
ADD COLUMN IF NOT EXISTS convertion_unit VARCHAR(20),
|
||||||
|
ADD COLUMN IF NOT EXISTS weight_per_convertion NUMERIC(15, 3),
|
||||||
|
ADD COLUMN IF NOT EXISTS week INTEGER;
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
DECLARE
|
||||||
|
fallback_fcr_id BIGINT;
|
||||||
|
BEGIN
|
||||||
|
IF NOT EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'project_flocks'
|
||||||
|
AND column_name = 'fcr_id'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
ADD COLUMN fcr_id BIGINT;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
SELECT id INTO fallback_fcr_id
|
||||||
|
FROM fcrs
|
||||||
|
ORDER BY id ASC
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
IF fallback_fcr_id IS NOT NULL THEN
|
||||||
|
UPDATE project_flocks
|
||||||
|
SET fcr_id = fallback_fcr_id
|
||||||
|
WHERE fcr_id IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
ALTER COLUMN fcr_id SET NOT NULL;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'project_flocks_fcr_id_fkey'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
DROP CONSTRAINT project_flocks_fcr_id_fkey;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
ADD CONSTRAINT project_flocks_fcr_id_fkey
|
||||||
|
FOREIGN KEY (fcr_id) REFERENCES fcrs(id)
|
||||||
|
ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
DO $$
|
||||||
|
BEGIN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM information_schema.columns
|
||||||
|
WHERE table_schema = 'public'
|
||||||
|
AND table_name = 'project_flocks'
|
||||||
|
AND column_name = 'fcr_id'
|
||||||
|
) THEN
|
||||||
|
IF EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM pg_constraint
|
||||||
|
WHERE conname = 'project_flocks_fcr_id_fkey'
|
||||||
|
) THEN
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
DROP CONSTRAINT project_flocks_fcr_id_fkey;
|
||||||
|
END IF;
|
||||||
|
|
||||||
|
ALTER TABLE project_flocks
|
||||||
|
DROP COLUMN fcr_id;
|
||||||
|
END IF;
|
||||||
|
END $$;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -11,6 +11,7 @@ type AdjustmentStock struct {
|
|||||||
PendingQty float64 `gorm:"column:pending_qty;default:0"`
|
PendingQty float64 `gorm:"column:pending_qty;default:0"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
|
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime"`
|
||||||
|
AdjNumber string `gorm:"column:adj_number;uniqueIndex;not null"`
|
||||||
|
|
||||||
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
ProductWarehouse *ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
StockLog *StockLog `gorm:"polymorphic:Loggable;polymorphicType:LoggableType;polymorphicId:LoggableId;polymorphicValue:ADJUSTMENT"`
|
StockLog *StockLog `gorm:"polymorphic:Loggable;polymorphicType:LoggableType;polymorphicId:LoggableId;polymorphicValue:ADJUSTMENT"`
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type Marketing struct {
|
|||||||
SoDate time.Time `gorm:"type:date;not null"`
|
SoDate time.Time `gorm:"type:date;not null"`
|
||||||
SalesPersonId uint `gorm:"not null"`
|
SalesPersonId uint `gorm:"not null"`
|
||||||
Notes string `gorm:"type:text"`
|
Notes string `gorm:"type:text"`
|
||||||
|
MarketingType string `gorm:"type:varchar(50)"`
|
||||||
CreatedBy uint `gorm:"not null"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
CreatedAt time.Time `gorm:"autoCreateTime"`
|
CreatedAt time.Time `gorm:"autoCreateTime"`
|
||||||
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
UpdatedAt time.Time `gorm:"autoUpdateTime"`
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
package entities
|
package entities
|
||||||
|
|
||||||
type MarketingProduct struct {
|
type MarketingProduct struct {
|
||||||
Id uint `gorm:"primaryKey;autoIncrement"`
|
Id uint `gorm:"primaryKey;autoIncrement"`
|
||||||
MarketingId uint `gorm:"not null"`
|
MarketingId uint `gorm:"not null"`
|
||||||
ProductWarehouseId uint `gorm:"not null"`
|
ProductWarehouseId uint `gorm:"not null"`
|
||||||
Qty float64 `gorm:"type:numeric(15,3);not null"`
|
Qty float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
UnitPrice float64 `gorm:"type:numeric(15,3);not null"`
|
ConvertionUnit *string `gorm:"type:varchar(20)"`
|
||||||
AvgWeight float64 `gorm:"type:numeric(15,3);not null"`
|
WeightPerConvertion *float64 `gorm:"type:numeric(15,3)"`
|
||||||
TotalWeight float64 `gorm:"type:numeric(15,3);not null"`
|
Week *int `gorm:"type:integer"`
|
||||||
TotalPrice float64 `gorm:"type:numeric(15,3);not null"`
|
UnitPrice float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
AvgWeight float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
TotalWeight float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
TotalPrice float64 `gorm:"type:numeric(15,3);not null"`
|
||||||
|
|
||||||
Marketing Marketing `gorm:"foreignKey:MarketingId;references:Id"`
|
Marketing Marketing `gorm:"foreignKey:MarketingId;references:Id"`
|
||||||
ProductWarehouse ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
ProductWarehouse ProductWarehouse `gorm:"foreignKey:ProductWarehouseId;references:Id"`
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ type ProjectFlock struct {
|
|||||||
FlockName string `gorm:"type:varchar(255);not null;uniqueIndex"`
|
FlockName string `gorm:"type:varchar(255);not null;uniqueIndex"`
|
||||||
AreaId uint `gorm:"not null"`
|
AreaId uint `gorm:"not null"`
|
||||||
Category string `gorm:"type:varchar(20);not null"`
|
Category string `gorm:"type:varchar(20);not null"`
|
||||||
FcrId uint `gorm:"not null"`
|
|
||||||
ProductionStandardId uint `gorm:"column:production_standard_id"`
|
ProductionStandardId uint `gorm:"column:production_standard_id"`
|
||||||
LocationId uint `gorm:"not null"`
|
LocationId uint `gorm:"not null"`
|
||||||
CreatedBy uint `gorm:"not null"`
|
CreatedBy uint `gorm:"not null"`
|
||||||
@@ -20,7 +19,6 @@ type ProjectFlock struct {
|
|||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
|
||||||
Area Area `gorm:"foreignKey:AreaId;references:Id"`
|
Area Area `gorm:"foreignKey:AreaId;references:Id"`
|
||||||
Fcr Fcr `gorm:"foreignKey:FcrId;references:Id"`
|
|
||||||
ProductionStandard ProductionStandard `gorm:"foreignKey:ProductionStandardId;references:Id"`
|
ProductionStandard ProductionStandard `gorm:"foreignKey:ProductionStandardId;references:Id"`
|
||||||
Location Location `gorm:"foreignKey:LocationId;references:Id"`
|
Location Location `gorm:"foreignKey:LocationId;references:Id"`
|
||||||
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
CreatedUser User `gorm:"foreignKey:CreatedBy;references:Id"`
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ type ClosingRepository interface {
|
|||||||
SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error)
|
SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error)
|
||||||
SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error)
|
SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error)
|
||||||
SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error)
|
SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error)
|
||||||
GetFcrStandardsByFcrID(ctx context.Context, fcrID uint) ([]entity.FcrStandard, error)
|
|
||||||
GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error)
|
GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error)
|
||||||
FetchSapronakIncoming(ctx context.Context, kandangID uint) ([]SapronakIncomingRow, error)
|
FetchSapronakIncoming(ctx context.Context, kandangID uint) ([]SapronakIncomingRow, error)
|
||||||
FetchSapronakIncomingDetails(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, error)
|
FetchSapronakIncomingDetails(ctx context.Context, kandangID uint) (map[uint][]SapronakDetailRow, error)
|
||||||
@@ -102,12 +101,12 @@ func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params Sapronak
|
|||||||
if len(params.WarehouseIDs) == 0 {
|
if len(params.WarehouseIDs) == 0 {
|
||||||
return []SapronakRow{}, 0, nil
|
return []SapronakRow{}, 0, nil
|
||||||
}
|
}
|
||||||
unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL)
|
unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL, sapronakIncomingAdjustmentsSQL)
|
||||||
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs, params.WarehouseIDs)
|
||||||
case validation.SapronakTypeOutgoing:
|
case validation.SapronakTypeOutgoing:
|
||||||
if len(params.WarehouseIDs) > 0 {
|
if len(params.WarehouseIDs) > 0 {
|
||||||
unionParts = append(unionParts, sapronakOutgoingTransfersSQL)
|
unionParts = append(unionParts, sapronakOutgoingTransfersSQL, sapronakOutgoingAdjustmentsSQL)
|
||||||
args = append(args, params.WarehouseIDs)
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
||||||
}
|
}
|
||||||
if len(params.ProjectFlockKandangIDs) > 0 {
|
if len(params.ProjectFlockKandangIDs) > 0 {
|
||||||
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
||||||
@@ -174,12 +173,12 @@ func (r *ClosingRepositoryImpl) GetSapronakSummary(ctx context.Context, params S
|
|||||||
if len(params.WarehouseIDs) == 0 {
|
if len(params.WarehouseIDs) == 0 {
|
||||||
return []SapronakSummaryRow{}, nil
|
return []SapronakSummaryRow{}, nil
|
||||||
}
|
}
|
||||||
unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL)
|
unionParts = append(unionParts, sapronakIncomingPurchasesSQL, sapronakIncomingTransfersSQL, sapronakIncomingAdjustmentsSQL)
|
||||||
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs, params.WarehouseIDs)
|
||||||
case validation.SapronakTypeOutgoing:
|
case validation.SapronakTypeOutgoing:
|
||||||
if len(params.WarehouseIDs) > 0 {
|
if len(params.WarehouseIDs) > 0 {
|
||||||
unionParts = append(unionParts, sapronakOutgoingTransfersSQL)
|
unionParts = append(unionParts, sapronakOutgoingTransfersSQL, sapronakOutgoingAdjustmentsSQL)
|
||||||
args = append(args, params.WarehouseIDs)
|
args = append(args, params.WarehouseIDs, params.WarehouseIDs)
|
||||||
}
|
}
|
||||||
if len(params.ProjectFlockKandangIDs) > 0 {
|
if len(params.ProjectFlockKandangIDs) > 0 {
|
||||||
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
unionParts = append(unionParts, sapronakOutgoingMarketingsSQL)
|
||||||
@@ -393,22 +392,6 @@ func (r *ClosingRepositoryImpl) SumRecordingEggQtyByProjectFlockKandangIDsAndFla
|
|||||||
return agg.TotalQty, nil
|
return agg.TotalQty, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) GetFcrStandardsByFcrID(ctx context.Context, fcrID uint) ([]entity.FcrStandard, error) {
|
|
||||||
if fcrID == 0 {
|
|
||||||
return []entity.FcrStandard{}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var standards []entity.FcrStandard
|
|
||||||
if err := r.DB().WithContext(ctx).
|
|
||||||
Where("fcr_id = ?", fcrID).
|
|
||||||
Order("weight ASC").
|
|
||||||
Find(&standards).Error; err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return standards, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) {
|
func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) {
|
||||||
db := r.DB().WithContext(ctx)
|
db := r.DB().WithContext(ctx)
|
||||||
|
|
||||||
@@ -456,7 +439,7 @@ SELECT
|
|||||||
COALESCE(pi.received_date, '1970-01-01') AS sort_date,
|
COALESCE(pi.received_date, '1970-01-01') AS sort_date,
|
||||||
COALESCE(TO_CHAR(pi.received_date, 'DD-Mon-YYYY'), '') AS date_text,
|
COALESCE(TO_CHAR(pi.received_date, 'DD-Mon-YYYY'), '') AS date_text,
|
||||||
COALESCE(p.po_number, '') AS reference_number,
|
COALESCE(p.po_number, '') AS reference_number,
|
||||||
'Purchase' AS transaction_type,
|
'Pembelian' AS transaction_type,
|
||||||
prod.name AS product_name,
|
prod.name AS product_name,
|
||||||
COALESCE((
|
COALESCE((
|
||||||
SELECT string_agg(
|
SELECT string_agg(
|
||||||
@@ -505,7 +488,7 @@ SELECT
|
|||||||
st.transfer_date AS sort_date,
|
st.transfer_date AS sort_date,
|
||||||
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
st.movement_number AS reference_number,
|
st.movement_number AS reference_number,
|
||||||
'Internal Transfer In' AS transaction_type,
|
'Mutasi' AS transaction_type,
|
||||||
prod.name AS product_name,
|
prod.name AS product_name,
|
||||||
COALESCE((
|
COALESCE((
|
||||||
SELECT string_agg(
|
SELECT string_agg(
|
||||||
@@ -549,13 +532,63 @@ JOIN uoms u ON u.id = prod.uom_id
|
|||||||
WHERE st.to_warehouse_id IN ?
|
WHERE st.to_warehouse_id IN ?
|
||||||
`
|
`
|
||||||
|
|
||||||
|
sapronakIncomingAdjustmentsSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(ast.id AS BIGINT) AS id,
|
||||||
|
ast.created_at AS sort_date,
|
||||||
|
COALESCE(TO_CHAR(ast.created_at, 'DD-Mon-YYYY'), '') AS date_text,
|
||||||
|
COALESCE(ast.adj_number, '') AS reference_number,
|
||||||
|
'Adjustment stock' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(
|
||||||
|
f.name,
|
||||||
|
' ' ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
f.name
|
||||||
|
)
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(
|
||||||
|
f.name,
|
||||||
|
' ' ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
f.name
|
||||||
|
)
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
'-' AS source_warehouse,
|
||||||
|
COALESCE(w.name, '') AS destination_warehouse,
|
||||||
|
'' AS destination,
|
||||||
|
COALESCE(ast.total_qty, 0) AS quantity,
|
||||||
|
u.id AS unit_id,
|
||||||
|
u.name AS unit,
|
||||||
|
'-' AS notes
|
||||||
|
FROM adjustment_stocks ast
|
||||||
|
JOIN product_warehouses pw ON pw.id = ast.product_warehouse_id
|
||||||
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
||||||
|
JOIN products prod ON prod.id = pw.product_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
WHERE pw.warehouse_id IN ?
|
||||||
|
AND COALESCE(ast.total_qty, 0) <> 0
|
||||||
|
`
|
||||||
|
|
||||||
sapronakOutgoingTransfersSQL = `
|
sapronakOutgoingTransfersSQL = `
|
||||||
SELECT
|
SELECT
|
||||||
CAST(st.id AS BIGINT) AS id,
|
CAST(st.id AS BIGINT) AS id,
|
||||||
st.transfer_date AS sort_date,
|
st.transfer_date AS sort_date,
|
||||||
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
TO_CHAR(st.transfer_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
st.movement_number AS reference_number,
|
st.movement_number AS reference_number,
|
||||||
'Internal Transfer Out' AS transaction_type,
|
'Mutasi' AS transaction_type,
|
||||||
prod.name AS product_name,
|
prod.name AS product_name,
|
||||||
COALESCE((
|
COALESCE((
|
||||||
SELECT string_agg(
|
SELECT string_agg(
|
||||||
@@ -599,13 +632,70 @@ JOIN uoms u ON u.id = prod.uom_id
|
|||||||
WHERE st.from_warehouse_id IN ?
|
WHERE st.from_warehouse_id IN ?
|
||||||
`
|
`
|
||||||
|
|
||||||
|
sapronakOutgoingAdjustmentsSQL = `
|
||||||
|
SELECT
|
||||||
|
CAST(ast.id AS BIGINT) AS id,
|
||||||
|
ast.created_at AS sort_date,
|
||||||
|
COALESCE(TO_CHAR(ast.created_at, 'DD-Mon-YYYY'), '') AS date_text,
|
||||||
|
COALESCE(ast.adj_number, '') AS reference_number,
|
||||||
|
'Adjustment stock' AS transaction_type,
|
||||||
|
prod.name AS product_name,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(
|
||||||
|
f.name,
|
||||||
|
' ' ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
f.name
|
||||||
|
)
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_category,
|
||||||
|
COALESCE((
|
||||||
|
SELECT string_agg(
|
||||||
|
f.name,
|
||||||
|
' ' ORDER BY
|
||||||
|
CASE
|
||||||
|
WHEN UPPER(f.name) IN ('DOC', 'PAKAN', 'OVK', 'PULLET') THEN 0
|
||||||
|
ELSE 1
|
||||||
|
END,
|
||||||
|
f.name
|
||||||
|
)
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_type = 'products' AND f.flagable_id = prod.id
|
||||||
|
), '') AS product_sub_category,
|
||||||
|
COALESCE(w.name, '') AS source_warehouse,
|
||||||
|
'-' AS destination_warehouse,
|
||||||
|
'' AS destination,
|
||||||
|
COALESCE(ast.usage_qty, 0) AS quantity,
|
||||||
|
u.id AS unit_id,
|
||||||
|
u.name AS unit,
|
||||||
|
'-' AS notes
|
||||||
|
FROM adjustment_stocks ast
|
||||||
|
JOIN product_warehouses pw ON pw.id = ast.product_warehouse_id
|
||||||
|
JOIN warehouses w ON w.id = pw.warehouse_id
|
||||||
|
JOIN products prod ON prod.id = pw.product_id
|
||||||
|
JOIN uoms u ON u.id = prod.uom_id
|
||||||
|
WHERE pw.warehouse_id IN ?
|
||||||
|
AND COALESCE(ast.usage_qty, 0) <> 0
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM flags f
|
||||||
|
WHERE f.flagable_id = pw.product_id
|
||||||
|
AND f.flagable_type = 'products'
|
||||||
|
AND UPPER(f.name) NOT IN ('DOC', 'LAYER', 'PULLET', 'AYAM-AFKIR', 'AYAM-MATI', 'AYAM-CULLING', 'TELUR-UTUH', 'TELUR-PECAH', 'TELUR-PUTIH', 'TELUR-RETAK')
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
sapronakOutgoingMarketingsSQL = `
|
sapronakOutgoingMarketingsSQL = `
|
||||||
SELECT
|
SELECT
|
||||||
CAST(mp.id AS BIGINT) AS id,
|
CAST(mp.id AS BIGINT) AS id,
|
||||||
m.so_date AS sort_date,
|
m.so_date AS sort_date,
|
||||||
TO_CHAR(m.so_date, 'DD-Mon-YYYY') AS date_text,
|
TO_CHAR(m.so_date, 'DD-Mon-YYYY') AS date_text,
|
||||||
m.so_number AS reference_number,
|
m.so_number AS reference_number,
|
||||||
'Trading Sales' AS transaction_type,
|
'Penjualan' AS transaction_type,
|
||||||
prod.name AS product_name,
|
prod.name AS product_name,
|
||||||
COALESCE((
|
COALESCE((
|
||||||
SELECT string_agg(
|
SELECT string_agg(
|
||||||
@@ -653,7 +743,7 @@ WHERE pw.project_flock_kandang_id IN ?
|
|||||||
FROM flags f
|
FROM flags f
|
||||||
WHERE f.flagable_id = pw.product_id
|
WHERE f.flagable_id = pw.product_id
|
||||||
AND f.flagable_type = 'products'
|
AND f.flagable_type = 'products'
|
||||||
AND UPPER(f.name) NOT IN ('DOC', 'LAYER', 'PULLET')
|
AND UPPER(f.name) NOT IN ('DOC', 'LAYER', 'PULLET', 'AYAM-AFKIR', 'AYAM-MATI', 'AYAM-CULLING', 'TELUR-UTUH', 'TELUR-PECAH', 'TELUR-PUTIH', 'TELUR-RETAK')
|
||||||
)
|
)
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -836,14 +836,6 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
|
|
||||||
finalPopulation := population - claimCulling
|
finalPopulation := population - claimCulling
|
||||||
|
|
||||||
var standards []entity.FcrStandard
|
|
||||||
if project.FcrId > 0 {
|
|
||||||
standards, err = s.Repository.GetFcrStandardsByFcrID(c.Context(), project.FcrId)
|
|
||||||
if err != nil {
|
|
||||||
s.Log.Errorf("Failed to fetch FCR standards for project flock %d: %+v", projectFlockID, err)
|
|
||||||
return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch FCR standard data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
age, err := s.calculateAverageSalesAge(c.Context(), projectFlockID, kandangID)
|
age, err := s.calculateAverageSalesAge(c.Context(), projectFlockID, kandangID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Log.Errorf("Failed to calculate sales age for project flock %d: %+v", projectFlockID, err)
|
s.Log.Errorf("Failed to calculate sales age for project flock %d: %+v", projectFlockID, err)
|
||||||
@@ -893,7 +885,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
chickenDepletion = 0
|
chickenDepletion = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards)
|
chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age)
|
||||||
if fcrActFromRecording != nil {
|
if fcrActFromRecording != nil {
|
||||||
chickenPerformance.FcrAct = *fcrActFromRecording
|
chickenPerformance.FcrAct = *fcrActFromRecording
|
||||||
}
|
}
|
||||||
@@ -943,7 +935,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
eggDepletion = 0
|
eggDepletion = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards)
|
eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age)
|
||||||
if fcrActFromRecording != nil {
|
if fcrActFromRecording != nil {
|
||||||
eggPerf.FcrAct = *fcrActFromRecording
|
eggPerf.FcrAct = *fcrActFromRecording
|
||||||
}
|
}
|
||||||
@@ -1001,10 +993,10 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint
|
|||||||
performance.EggMass = eggMass
|
performance.EggMass = eggMass
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
performance.DeffFcr = performance.FcrStd - performance.FcrAct
|
|
||||||
if productionStandardDetail != nil {
|
if productionStandardDetail != nil {
|
||||||
if productionStandardDetail.StandardFCR != nil {
|
if productionStandardDetail.StandardFCR != nil {
|
||||||
performance.FcrStd = *productionStandardDetail.StandardFCR
|
performance.FcrStd = *productionStandardDetail.StandardFCR
|
||||||
|
performance.DeffFcr = performance.FcrStd - performance.FcrAct
|
||||||
}
|
}
|
||||||
if !isGrowing {
|
if !isGrowing {
|
||||||
if productionStandardDetail.TargetHenDayProduction != nil {
|
if productionStandardDetail.TargetHenDayProduction != nil {
|
||||||
@@ -1091,8 +1083,8 @@ func (s closingService) determineProductionWeek(ctx context.Context, projectFloc
|
|||||||
return week, nil
|
return week, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO {
|
func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64) dto.ClosingPerformanceDTO {
|
||||||
mortalityStd, fcrStd := closestFcrValues(standards, averageWeight)
|
mortalityStd, fcrStd := 0.0, 0.0
|
||||||
|
|
||||||
fcrAct := 0.0
|
fcrAct := 0.0
|
||||||
if totalWeight > 0 {
|
if totalWeight > 0 {
|
||||||
@@ -1124,21 +1116,3 @@ func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopul
|
|||||||
AwgAct: awg,
|
AwgAct: awg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func closestFcrValues(standards []entity.FcrStandard, averageWeight float64) (float64, float64) {
|
|
||||||
if len(standards) == 0 || averageWeight <= 0 {
|
|
||||||
return 0, 0
|
|
||||||
}
|
|
||||||
|
|
||||||
closest := standards[0]
|
|
||||||
minDiff := math.Abs(closest.Weight - averageWeight)
|
|
||||||
for _, std := range standards[1:] {
|
|
||||||
diff := math.Abs(std.Weight - averageWeight)
|
|
||||||
if diff < minDiff {
|
|
||||||
minDiff = diff
|
|
||||||
closest = std
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return closest.Mortality, closest.FcrNumber
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -444,30 +444,6 @@ func (r *DashboardRepositoryImpl) standardIDSubquery(filters *validation.Dashboa
|
|||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *DashboardRepositoryImpl) standardSourceSubquery(filters *validation.DashboardFilter) *gorm.DB {
|
|
||||||
db := r.DB().
|
|
||||||
Table("project_flocks AS pf").
|
|
||||||
Select("DISTINCT pf.production_standard_id, pf.fcr_id").
|
|
||||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id").
|
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
||||||
Where("pf.production_standard_id > 0").
|
|
||||||
Where("pf.fcr_id > 0")
|
|
||||||
|
|
||||||
if filters != nil {
|
|
||||||
if len(filters.FlockIds) > 0 {
|
|
||||||
db = db.Where("pf.id IN ?", filters.FlockIds)
|
|
||||||
}
|
|
||||||
if len(filters.KandangIds) > 0 {
|
|
||||||
db = db.Where("k.id IN ?", filters.KandangIds)
|
|
||||||
}
|
|
||||||
if len(filters.LokasiIds) > 0 {
|
|
||||||
db = db.Where("k.location_id IN ?", filters.LokasiIds)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *DashboardRepositoryImpl) GetComparisonSeries(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonSeries, error) {
|
func (r *DashboardRepositoryImpl) GetComparisonSeries(ctx context.Context, start, end time.Time, filters *validation.DashboardFilter, comparisonType string) ([]ComparisonSeries, error) {
|
||||||
seriesExpr, labelExpr, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType)
|
seriesExpr, labelExpr, groupExpr, orderExpr, err := comparisonSeriesColumns(comparisonType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -24,46 +24,81 @@ func NewTransactionController(transactionService service.TransactionService) *Tr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *TransactionController) GetAll(c *fiber.Ctx) error {
|
func (u *TransactionController) GetAll(c *fiber.Ctx) error {
|
||||||
parseOptionalUint := func(key string) (*uint, error) {
|
parseUintListParam := func(key string) ([]uint, error) {
|
||||||
raw := strings.TrimSpace(c.Query(key, ""))
|
raw := strings.TrimSpace(c.Query(key, ""))
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
parsed, err := strconv.ParseUint(raw, 10, 64)
|
parts := strings.Split(raw, ",")
|
||||||
if err != nil {
|
ids := make([]uint, 0, len(parts))
|
||||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid "+key)
|
for _, part := range parts {
|
||||||
|
trimmed := strings.TrimSpace(part)
|
||||||
|
if trimmed == "" {
|
||||||
|
return nil, strconv.ErrSyntax
|
||||||
|
}
|
||||||
|
parsed, err := strconv.ParseUint(trimmed, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if parsed == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ids = append(ids, uint(parsed))
|
||||||
}
|
}
|
||||||
if parsed == 0 {
|
if len(ids) == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
value := uint(parsed)
|
return ids, nil
|
||||||
return &value, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bankId, err := parseOptionalUint("bank_id")
|
parseStringListParam := func(key string) ([]string, error) {
|
||||||
if err != nil {
|
raw := strings.TrimSpace(c.Query(key, ""))
|
||||||
return err
|
if raw == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(raw, ",")
|
||||||
|
values := make([]string, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
trimmed := strings.TrimSpace(part)
|
||||||
|
if trimmed == "" {
|
||||||
|
return nil, strconv.ErrSyntax
|
||||||
|
}
|
||||||
|
values = append(values, trimmed)
|
||||||
|
}
|
||||||
|
if len(values) == 0 {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return values, nil
|
||||||
}
|
}
|
||||||
customerId, err := parseOptionalUint("customer_id")
|
|
||||||
|
bankIDs, err := parseUintListParam("bank_ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid bank_ids")
|
||||||
}
|
}
|
||||||
supplierId, err := parseOptionalUint("supplier_id")
|
customerIDs, err := parseUintListParam("customer_ids")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid customer_ids")
|
||||||
|
}
|
||||||
|
supplierIDs, err := parseUintListParam("supplier_ids")
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid supplier_ids")
|
||||||
|
}
|
||||||
|
transactionTypes, err := parseStringListParam("transaction_types")
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid transaction_types")
|
||||||
}
|
}
|
||||||
|
|
||||||
query := &validation.Query{
|
query := &validation.Query{
|
||||||
Page: c.QueryInt("page", 1),
|
Page: c.QueryInt("page", 1),
|
||||||
Limit: c.QueryInt("limit", 10),
|
Limit: c.QueryInt("limit", 10),
|
||||||
Search: c.Query("search", ""),
|
Search: c.Query("search", ""),
|
||||||
TransactionType: c.Query("transaction_type", ""),
|
TransactionTypes: transactionTypes,
|
||||||
BankId: bankId,
|
BankIDs: bankIDs,
|
||||||
CustomerId: customerId,
|
CustomerIDs: customerIDs,
|
||||||
SupplierId: supplierId,
|
SupplierIDs: supplierIDs,
|
||||||
SortDate: c.Query("sort_date", ""),
|
SortDate: c.Query("sort_date", ""),
|
||||||
StartDate: c.Query("start_date", ""),
|
StartDate: c.Query("start_date", ""),
|
||||||
EndDate: c.Query("end_date", ""),
|
EndDate: c.Query("end_date", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Page < 1 || query.Limit < 1 {
|
if query.Page < 1 || query.Limit < 1 {
|
||||||
|
|||||||
@@ -74,33 +74,58 @@ func (s transactionService) GetAll(c *fiber.Ctx, params *validation.Query) ([]en
|
|||||||
|
|
||||||
if params.Search != "" {
|
if params.Search != "" {
|
||||||
like := "%" + strings.ToLower(strings.TrimSpace(params.Search)) + "%"
|
like := "%" + strings.ToLower(strings.TrimSpace(params.Search)) + "%"
|
||||||
|
db = db.Joins(
|
||||||
|
"LEFT JOIN customers ON customers.id = payments.party_id AND payments.party_type = ? AND customers.deleted_at IS NULL",
|
||||||
|
string(utils.PaymentPartyCustomer),
|
||||||
|
).Joins(
|
||||||
|
"LEFT JOIN suppliers ON suppliers.id = payments.party_id AND payments.party_type = ? AND suppliers.deleted_at IS NULL",
|
||||||
|
string(utils.PaymentPartySupplier),
|
||||||
|
).Joins(
|
||||||
|
"LEFT JOIN banks ON banks.id = payments.bank_id AND banks.deleted_at IS NULL",
|
||||||
|
)
|
||||||
db = db.Where(
|
db = db.Where(
|
||||||
`LOWER(payment_code) LIKE ? OR
|
`LOWER(payment_code) LIKE ? OR
|
||||||
LOWER(COALESCE(reference_number, '')) LIKE ? OR
|
LOWER(COALESCE(reference_number, '')) LIKE ? OR
|
||||||
LOWER(COALESCE(transaction_type, '')) LIKE ? OR
|
LOWER(COALESCE(transaction_type, '')) LIKE ? OR
|
||||||
LOWER(COALESCE(notes, '')) LIKE ?`,
|
LOWER(COALESCE(notes, '')) LIKE ? OR
|
||||||
like, like, like, like,
|
LOWER(COALESCE(customers.name, '')) LIKE ? OR
|
||||||
|
LOWER(COALESCE(suppliers.name, '')) LIKE ? OR
|
||||||
|
LOWER(COALESCE(banks.name, '')) LIKE ?`,
|
||||||
|
like, like, like, like, like, like, like,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if strings.TrimSpace(params.TransactionType) != "" {
|
if len(params.TransactionTypes) > 0 {
|
||||||
db = db.Where("transaction_type = ?", strings.ToUpper(strings.TrimSpace(params.TransactionType)))
|
types := make([]string, 0, len(params.TransactionTypes))
|
||||||
|
for _, transactionType := range params.TransactionTypes {
|
||||||
|
normalized := strings.ToUpper(strings.TrimSpace(transactionType))
|
||||||
|
if normalized == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
types = append(types, normalized)
|
||||||
|
}
|
||||||
|
if len(types) > 0 {
|
||||||
|
db = db.Where("transaction_type IN ?", types)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.BankId != nil {
|
if len(params.BankIDs) > 0 {
|
||||||
db = db.Where("bank_id = ?", *params.BankId)
|
db = db.Where("bank_id IN ?", params.BankIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if params.CustomerId != nil && params.SupplierId != nil {
|
customerIDs := params.CustomerIDs
|
||||||
|
supplierIDs := params.SupplierIDs
|
||||||
|
|
||||||
|
if len(customerIDs) > 0 && len(supplierIDs) > 0 {
|
||||||
db = db.Where(
|
db = db.Where(
|
||||||
"(party_type = ? AND party_id = ?) OR (party_type = ? AND party_id = ?)",
|
"(party_type = ? AND party_id IN ?) OR (party_type = ? AND party_id IN ?)",
|
||||||
string(utils.PaymentPartyCustomer), *params.CustomerId,
|
string(utils.PaymentPartyCustomer), customerIDs,
|
||||||
string(utils.PaymentPartySupplier), *params.SupplierId,
|
string(utils.PaymentPartySupplier), supplierIDs,
|
||||||
)
|
)
|
||||||
} else if params.CustomerId != nil {
|
} else if len(customerIDs) > 0 {
|
||||||
db = db.Where("party_type = ? AND party_id = ?", string(utils.PaymentPartyCustomer), *params.CustomerId)
|
db = db.Where("party_type = ? AND party_id IN ?", string(utils.PaymentPartyCustomer), customerIDs)
|
||||||
} else if params.SupplierId != nil {
|
} else if len(supplierIDs) > 0 {
|
||||||
db = db.Where("party_type = ? AND party_id = ?", string(utils.PaymentPartySupplier), *params.SupplierId)
|
db = db.Where("party_type = ? AND party_id IN ?", string(utils.PaymentPartySupplier), supplierIDs)
|
||||||
}
|
}
|
||||||
|
|
||||||
if startDate != nil {
|
if startDate != nil {
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
package validation
|
package validation
|
||||||
|
|
||||||
type Create struct {
|
type Create struct {
|
||||||
Name string `json:"name" validate:"required_strict,min=3"`
|
Name string `json:"name" validate:"required_strict,min=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
Name *string `json:"name,omitempty" validate:"omitempty"`
|
Name *string `json:"name,omitempty" validate:"omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Query struct {
|
type Query struct {
|
||||||
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
Page int `query:"page" validate:"omitempty,number,min=1,gt=0"`
|
||||||
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
Limit int `query:"limit" validate:"omitempty,number,min=1,max=100,gt=0"`
|
||||||
Search string `query:"search" validate:"omitempty,max=50"`
|
Search string `query:"search" validate:"omitempty,max=50"`
|
||||||
TransactionType string `query:"transaction_type" validate:"omitempty,max=50"`
|
TransactionTypes []string `query:"transaction_types" validate:"omitempty,dive,max=50"`
|
||||||
BankId *uint `query:"bank_id" validate:"omitempty,number,gt=0"`
|
BankIDs []uint `query:"bank_ids" validate:"omitempty,dive,gt=0"`
|
||||||
CustomerId *uint `query:"customer_id" validate:"omitempty,number,gt=0"`
|
CustomerIDs []uint `query:"customer_ids" validate:"omitempty,dive,gt=0"`
|
||||||
SupplierId *uint `query:"supplier_id" validate:"omitempty,number,gt=0"`
|
SupplierIDs []uint `query:"supplier_ids" validate:"omitempty,dive,gt=0"`
|
||||||
SortDate string `query:"sort_date" validate:"omitempty,oneof=created_at payment_date"`
|
SortDate string `query:"sort_date" validate:"omitempty,oneof=created_at payment_date"`
|
||||||
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package repositories
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
|
"gorm.io/gorm/clause"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AdjustmentStockRepository interface {
|
type AdjustmentStockRepository interface {
|
||||||
@@ -12,6 +16,7 @@ type AdjustmentStockRepository interface {
|
|||||||
GetByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.AdjustmentStock, error)
|
GetByID(ctx context.Context, id uint, modifier func(*gorm.DB) *gorm.DB) (*entity.AdjustmentStock, error)
|
||||||
WithTx(tx *gorm.DB) AdjustmentStockRepository
|
WithTx(tx *gorm.DB) AdjustmentStockRepository
|
||||||
DB() *gorm.DB
|
DB() *gorm.DB
|
||||||
|
GenerateSequentialNumber(ctx context.Context, prefix string) (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type adjustmentStockRepositoryImpl struct {
|
type adjustmentStockRepositoryImpl struct {
|
||||||
@@ -50,3 +55,71 @@ func (r *adjustmentStockRepositoryImpl) WithTx(tx *gorm.DB) AdjustmentStockRepos
|
|||||||
func (r *adjustmentStockRepositoryImpl) DB() *gorm.DB {
|
func (r *adjustmentStockRepositoryImpl) DB() *gorm.DB {
|
||||||
return r.db
|
return r.db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *adjustmentStockRepositoryImpl) GenerateSequentialNumber(ctx context.Context, prefix string) (string, error) {
|
||||||
|
var values []string
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Model(&entity.AdjustmentStock{}).
|
||||||
|
Where(fmt.Sprintf("%s ILIKE ?", "adj_number"), prefix+"%").
|
||||||
|
Select("adj_number").
|
||||||
|
Order(fmt.Sprintf("%s DESC", "adj_number")).
|
||||||
|
Limit(20).
|
||||||
|
Clauses(clause.Locking{Strength: "UPDATE"}).
|
||||||
|
Pluck("adj_number", &values).Error
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
next := 1
|
||||||
|
for _, value := range values {
|
||||||
|
if number, ok := parseNumericSuffix(value, prefix); ok {
|
||||||
|
next = number + 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxAttempts = 20
|
||||||
|
for attempt := 0; attempt < maxAttempts; attempt++ {
|
||||||
|
candidate := fmt.Sprintf("%s%0*d", prefix, 5, next)
|
||||||
|
exists, err := r.numberExists(ctx, r.db, candidate)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
return candidate, nil
|
||||||
|
}
|
||||||
|
next++
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("unable to generate unique %s", "adj_number")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *adjustmentStockRepositoryImpl) numberExists(ctx context.Context, db *gorm.DB, value string) (bool, error) {
|
||||||
|
var count int64
|
||||||
|
if err := db.WithContext(ctx).
|
||||||
|
Model(&entity.AdjustmentStock{}).
|
||||||
|
Where(fmt.Sprintf("%s = ?", "adj_number"), value).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseNumericSuffix(value, prefix string) (int, bool) {
|
||||||
|
if !strings.HasPrefix(value, prefix) {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
suffix := strings.TrimPrefix(value, prefix)
|
||||||
|
if suffix == "" {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
trimmed := strings.TrimLeft(suffix, "0")
|
||||||
|
if trimmed == "" {
|
||||||
|
trimmed = "0"
|
||||||
|
}
|
||||||
|
number, err := strconv.Atoi(trimmed)
|
||||||
|
if err != nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return number, true
|
||||||
|
}
|
||||||
|
|||||||
@@ -200,6 +200,11 @@ func (s *adjustmentService) Adjustment(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
adjustmentStock := &entity.AdjustmentStock{
|
adjustmentStock := &entity.AdjustmentStock{
|
||||||
ProductWarehouseId: productWarehouse.Id,
|
ProductWarehouseId: productWarehouse.Id,
|
||||||
}
|
}
|
||||||
|
code, err := s.AdjustmentStockRepository.GenerateSequentialNumber(ctx, utils.AdjustmentStockNumberPrefix)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
adjustmentStock.AdjNumber = code
|
||||||
if err := s.AdjustmentStockRepository.WithTx(tx).CreateOne(ctx, adjustmentStock, nil); err != nil {
|
if err := s.AdjustmentStockRepository.WithTx(tx).CreateOne(ctx, adjustmentStock, nil); err != nil {
|
||||||
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create adjustment stock record")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create adjustment stock record")
|
||||||
|
|||||||
+1
@@ -32,6 +32,7 @@ func (u *ProductWarehouseController) GetAll(c *fiber.Ctx) error {
|
|||||||
Flags: c.Query("flags", ""),
|
Flags: c.Query("flags", ""),
|
||||||
KandangId: uint(c.QueryInt("kandang_id", 0)),
|
KandangId: uint(c.QueryInt("kandang_id", 0)),
|
||||||
TransferContext: c.Query(utils.TransferContextKey, ""),
|
TransferContext: c.Query(utils.TransferContextKey, ""),
|
||||||
|
Type: c.Query("type", ""),
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Page < 1 || query.Limit < 1 {
|
if query.Page < 1 || query.Limit < 1 {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
|
productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto"
|
||||||
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// === DTO Structs ===
|
// === DTO Structs ===
|
||||||
@@ -22,6 +23,7 @@ type ProductWarehouseListDTO struct {
|
|||||||
Product *productDTO.ProductRelationDTO `json:"product,omitempty"`
|
Product *productDTO.ProductRelationDTO `json:"product,omitempty"`
|
||||||
Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
|
Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"`
|
||||||
ProjectFlockKandang *ProjectFlockKandangRelationDTO `json:"project_flock_kandang,omitempty"`
|
ProjectFlockKandang *ProjectFlockKandangRelationDTO `json:"project_flock_kandang,omitempty"`
|
||||||
|
Week int `json:"week"`
|
||||||
CreatedUser *UserRelationDTO `json:"created_user,omitempty"`
|
CreatedUser *UserRelationDTO `json:"created_user,omitempty"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
@@ -109,6 +111,22 @@ func ToProductWarehouseListDTO(e entity.ProductWarehouse) ProductWarehouseListDT
|
|||||||
}
|
}
|
||||||
|
|
||||||
dto.ProjectFlockKandang = pfkDTO
|
dto.ProjectFlockKandang = pfkDTO
|
||||||
|
|
||||||
|
// Calculate week for AYAM_PULLET/AYAM products
|
||||||
|
productFlags := make([]string, len(e.Product.Flags))
|
||||||
|
for i, f := range e.Product.Flags {
|
||||||
|
productFlags[i] = f.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
var category string
|
||||||
|
if e.ProjectFlockKandang.ProjectFlock.Id != 0 {
|
||||||
|
category = e.ProjectFlockKandang.ProjectFlock.Category
|
||||||
|
}
|
||||||
|
|
||||||
|
now := time.Now()
|
||||||
|
_, ageInWeeks := calculateAgeFromChickin(e.ProjectFlockKandang, &now, productFlags, category)
|
||||||
|
|
||||||
|
dto.Week = ageInWeeks
|
||||||
}
|
}
|
||||||
|
|
||||||
return dto
|
return dto
|
||||||
@@ -138,3 +156,58 @@ func ToProductWarehouseNestedDTO(e entity.ProductWarehouse) ProductWarehousNeste
|
|||||||
Warehouse: &warehouse,
|
Warehouse: &warehouse,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Helper function to calculate age from chickin (same logic as closingMarketing.dto.go)
|
||||||
|
func calculateAgeFromChickin(projectFlockKandang *entity.ProjectFlockKandang, currentDate *time.Time, productFlags []string, category string) (int, int) {
|
||||||
|
if projectFlockKandang == nil || currentDate == nil || len(projectFlockKandang.Chickins) == 0 {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return 0 for TRADING, TELUR, and AYAM flags (only AYAM_PULLET should have week)
|
||||||
|
for _, flag := range productFlags {
|
||||||
|
if flag == string(utils.FlagOVK) ||
|
||||||
|
flag == string(utils.FlagPakan) ||
|
||||||
|
flag == string(utils.FlagPreStarter) ||
|
||||||
|
flag == string(utils.FlagStarter) ||
|
||||||
|
flag == string(utils.FlagFinisher) ||
|
||||||
|
flag == string(utils.FlagObat) ||
|
||||||
|
flag == string(utils.FlagVitamin) ||
|
||||||
|
flag == string(utils.FlagKimia) ||
|
||||||
|
flag == string(utils.FlagEkspedisi) ||
|
||||||
|
flag == string(utils.FlagTelur) ||
|
||||||
|
flag == string(utils.FlagTelurUtuh) ||
|
||||||
|
flag == string(utils.FlagTelurPecah) ||
|
||||||
|
flag == string(utils.FlagTelurPutih) ||
|
||||||
|
flag == string(utils.FlagTelurRetak) ||
|
||||||
|
flag == string(utils.FlagAyamAfkir) ||
|
||||||
|
flag == string(utils.FlagAyamCulling) ||
|
||||||
|
flag == string(utils.FlagAyamMati) {
|
||||||
|
return 0, 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find earliest chickin date
|
||||||
|
earliestChickinDate := projectFlockKandang.Chickins[0].ChickInDate
|
||||||
|
for _, chickin := range projectFlockKandang.Chickins {
|
||||||
|
if chickin.ChickInDate.Before(earliestChickinDate) {
|
||||||
|
earliestChickinDate = chickin.ChickInDate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
diff := currentDate.Sub(earliestChickinDate)
|
||||||
|
ageInDays := int(diff.Hours() / 24)
|
||||||
|
|
||||||
|
var ageInWeeks int
|
||||||
|
if ageInDays <= 0 {
|
||||||
|
ageInWeeks = 0
|
||||||
|
} else {
|
||||||
|
if category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
ageInDays = ageInDays + 119
|
||||||
|
ageInWeeks = ((ageInDays - 1) / 7) + 1
|
||||||
|
} else {
|
||||||
|
ageInWeeks = ((ageInDays - 1) / 7) + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ageInDays, ageInWeeks
|
||||||
|
}
|
||||||
|
|||||||
+4
-3
@@ -168,9 +168,10 @@ func (r *ProductWarehouseRepositoryImpl) ApplyFlagsFilter(db *gorm.DB, flags []s
|
|||||||
}
|
}
|
||||||
|
|
||||||
return db.
|
return db.
|
||||||
Joins("JOIN products ON products.id = product_warehouses.product_id").
|
Joins("JOIN products p_flag ON p_flag.id = product_warehouses.product_id").
|
||||||
Joins("JOIN flags ON flags.flagable_id = products.id AND flags.flagable_type = ?", "products").
|
Joins("JOIN flags f_flag ON f_flag.flagable_id = p_flag.id AND f_flag.flagable_type = ?", "products").
|
||||||
Where("flags.name IN ?", flags)
|
Where("f_flag.name IN ?", flags).
|
||||||
|
Distinct()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ProductWarehouseRepositoryImpl) AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error {
|
func (r *ProductWarehouseRepositoryImpl) AdjustQuantities(ctx context.Context, deltas map[uint]float64, modifier func(*gorm.DB) *gorm.DB) error {
|
||||||
|
|||||||
+24
-2
@@ -46,7 +46,8 @@ func (s productWarehouseService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("Warehouse.Area").
|
Preload("Warehouse.Area").
|
||||||
Preload("Warehouse.Kandang").
|
Preload("Warehouse.Kandang").
|
||||||
Preload("ProjectFlockKandang").
|
Preload("ProjectFlockKandang").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock")
|
Preload("ProjectFlockKandang.ProjectFlock").
|
||||||
|
Preload("ProjectFlockKandang.Chickins")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) {
|
func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query) ([]entity.ProductWarehouse, int64, error) {
|
||||||
@@ -99,6 +100,12 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
|||||||
|
|
||||||
offset := (params.Page - 1) * params.Limit
|
offset := (params.Page - 1) * params.Limit
|
||||||
|
|
||||||
|
if params.Type != "" {
|
||||||
|
if !utils.IsValidMarketingType(params.Type) {
|
||||||
|
return nil, 0, fiber.NewError(fiber.StatusBadRequest, "Invalid marketing type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
cleanFlags := utils.ParseFlags(params.Flags)
|
cleanFlags := utils.ParseFlags(params.Flags)
|
||||||
|
|
||||||
productWarehouses, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
productWarehouses, total, err := s.Repository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB {
|
||||||
@@ -128,7 +135,22 @@ func (s productWarehouseService) GetAll(c *fiber.Ctx, params *validation.Query)
|
|||||||
db = db.Where("warehouse_id = ?", params.WarehouseId)
|
db = db.Where("warehouse_id = ?", params.WarehouseId)
|
||||||
}
|
}
|
||||||
|
|
||||||
db = s.Repository.ApplyFlagsFilter(db, cleanFlags)
|
if params.Type != "" {
|
||||||
|
switch params.Type {
|
||||||
|
case string(utils.MarketingTypeAyamPullet):
|
||||||
|
db = s.Repository.ApplyFlagsFilter(db, []string{string(utils.FlagDOC), string(utils.FlagPullet), string(utils.FlagLayer)})
|
||||||
|
case string(utils.MarketingTypeAyam):
|
||||||
|
db = s.Repository.ApplyFlagsFilter(db, []string{string(utils.FlagAyamAfkir), string(utils.FlagAyamCulling), string(utils.FlagAyamMati)})
|
||||||
|
case string(utils.MarketingTypeTelur):
|
||||||
|
db = s.Repository.ApplyFlagsFilter(db, []string{string(utils.FlagTelur), string(utils.FlagTelurUtuh), string(utils.FlagTelurPecah), string(utils.FlagTelurPutih), string(utils.FlagTelurRetak)})
|
||||||
|
case string(utils.MarketingTypeTrading):
|
||||||
|
db = s.Repository.ApplyFlagsFilter(db, []string{string(utils.FlagPakan), string(utils.FlagPreStarter), string(utils.FlagStarter), string(utils.FlagFinisher), string(utils.FlagOVK), string(utils.FlagObat), string(utils.FlagVitamin), string(utils.FlagKimia), string(utils.FlagEkspedisi)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cleanFlags) > 0 {
|
||||||
|
db = s.Repository.ApplyFlagsFilter(db, cleanFlags)
|
||||||
|
}
|
||||||
|
|
||||||
return db.Order("product_warehouses.id DESC")
|
return db.Order("product_warehouses.id DESC")
|
||||||
})
|
})
|
||||||
|
|||||||
+1
@@ -20,4 +20,5 @@ type Query struct {
|
|||||||
Flags string `query:"flags" validate:"omitempty"`
|
Flags string `query:"flags" validate:"omitempty"`
|
||||||
KandangId uint `query:"kandang_id" validate:"omitempty,number,min=1"`
|
KandangId uint `query:"kandang_id" validate:"omitempty,number,min=1"`
|
||||||
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
TransferContext string `query:"transfer_context" validate:"omitempty,oneof=inventory_transfer"`
|
||||||
|
Type string `query:"type" validate:"omitempty,oneof=AYAM TELUR TRADING AYAM_PULLET"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package dto
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"sort"
|
"sort"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -76,16 +77,21 @@ type DeliveryGroupDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type DeliveryMarketingProductDTO struct {
|
type DeliveryMarketingProductDTO struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
MarketingId uint `json:"marketing_id"`
|
MarketingId uint `json:"marketing_id"`
|
||||||
ProductWarehouseId uint `json:"product_warehouse_id"`
|
ProductWarehouseId uint `json:"product_warehouse_id"`
|
||||||
Qty float64 `json:"qty"`
|
MarketingType string `json:"marketing_type"`
|
||||||
UnitPrice float64 `json:"unit_price"`
|
Qty float64 `json:"qty"`
|
||||||
AvgWeight float64 `json:"avg_weight"`
|
UnitPrice float64 `json:"unit_price"`
|
||||||
TotalWeight float64 `json:"total_weight"`
|
AvgWeight float64 `json:"avg_weight"`
|
||||||
TotalPrice float64 `json:"total_price"`
|
TotalWeight float64 `json:"total_weight"`
|
||||||
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
TotalPrice float64 `json:"total_price"`
|
||||||
VehicleNumber string `json:"vehicle_number,omitempty"`
|
ConvertionUnit *string `json:"convertion_unit,omitempty"`
|
||||||
|
WeightPerConvertion *float64 `json:"weight_per_convertion,omitempty"`
|
||||||
|
TotalPeti *float64 `json:"total_peti,omitempty"`
|
||||||
|
Week *int `json:"week,omitempty"`
|
||||||
|
ProductWarehouse *productwarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
||||||
|
VehicleNumber string `json:"vehicle_number,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToMarketingRelationDTO(marketing *entity.Marketing) MarketingRelationDTO {
|
func ToMarketingRelationDTO(marketing *entity.Marketing) MarketingRelationDTO {
|
||||||
@@ -97,24 +103,36 @@ func ToMarketingRelationDTO(marketing *entity.Marketing) MarketingRelationDTO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToDeliveryMarketingProductDTO(e entity.MarketingProduct) DeliveryMarketingProductDTO {
|
func ToDeliveryMarketingProductDTO(e entity.MarketingProduct, marketingType string) DeliveryMarketingProductDTO {
|
||||||
var productWarehouse *productwarehouseDTO.ProductWarehousNestedDTO
|
var productWarehouse *productwarehouseDTO.ProductWarehousNestedDTO
|
||||||
if e.ProductWarehouse.Id != 0 {
|
if e.ProductWarehouse.Id != 0 {
|
||||||
mapped := productwarehouseDTO.ToProductWarehouseNestedDTO(e.ProductWarehouse)
|
mapped := productwarehouseDTO.ToProductWarehouseNestedDTO(e.ProductWarehouse)
|
||||||
productWarehouse = &mapped
|
productWarehouse = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate total_peti only for TELUR marketing type
|
||||||
|
var totalPeti *float64
|
||||||
|
if marketingType == "TELUR" && e.ConvertionUnit != nil && *e.ConvertionUnit == "PETI" && e.WeightPerConvertion != nil && *e.WeightPerConvertion > 0 {
|
||||||
|
calculated := math.Floor(e.TotalWeight / *e.WeightPerConvertion)
|
||||||
|
totalPeti = &calculated
|
||||||
|
}
|
||||||
|
|
||||||
return DeliveryMarketingProductDTO{
|
return DeliveryMarketingProductDTO{
|
||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
MarketingId: e.MarketingId,
|
MarketingId: e.MarketingId,
|
||||||
ProductWarehouseId: e.ProductWarehouseId,
|
ProductWarehouseId: e.ProductWarehouseId,
|
||||||
Qty: e.Qty,
|
MarketingType: marketingType,
|
||||||
UnitPrice: e.UnitPrice,
|
Qty: e.Qty,
|
||||||
AvgWeight: e.AvgWeight,
|
UnitPrice: e.UnitPrice,
|
||||||
TotalWeight: e.TotalWeight,
|
AvgWeight: e.AvgWeight,
|
||||||
TotalPrice: e.TotalPrice,
|
TotalWeight: e.TotalWeight,
|
||||||
ProductWarehouse: productWarehouse,
|
TotalPrice: e.TotalPrice,
|
||||||
VehicleNumber: getVehicleNumber(e),
|
ConvertionUnit: e.ConvertionUnit,
|
||||||
|
WeightPerConvertion: e.WeightPerConvertion,
|
||||||
|
TotalPeti: totalPeti,
|
||||||
|
Week: e.Week,
|
||||||
|
ProductWarehouse: productWarehouse,
|
||||||
|
VehicleNumber: getVehicleNumber(e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -161,7 +179,7 @@ func ToMarketingListDTO(marketing *entity.Marketing, deliveryProducts []entity.M
|
|||||||
if len(marketing.Products) > 0 {
|
if len(marketing.Products) > 0 {
|
||||||
salesOrderProducts = make([]DeliveryMarketingProductDTO, len(marketing.Products))
|
salesOrderProducts = make([]DeliveryMarketingProductDTO, len(marketing.Products))
|
||||||
for i, product := range marketing.Products {
|
for i, product := range marketing.Products {
|
||||||
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product)
|
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product, marketing.MarketingType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -201,7 +219,7 @@ func ToMarketingDetailDTO(marketing *entity.Marketing, deliveryProducts []entity
|
|||||||
if len(marketing.Products) > 0 {
|
if len(marketing.Products) > 0 {
|
||||||
salesOrderProducts = make([]DeliveryMarketingProductDTO, len(marketing.Products))
|
salesOrderProducts = make([]DeliveryMarketingProductDTO, len(marketing.Products))
|
||||||
for i, product := range marketing.Products {
|
for i, product := range marketing.Products {
|
||||||
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product)
|
salesOrderProducts[i] = ToDeliveryMarketingProductDTO(product, marketing.MarketingType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -10,13 +11,18 @@ import (
|
|||||||
// === DTO Structs ===
|
// === DTO Structs ===
|
||||||
|
|
||||||
type MarketingProductDTO struct {
|
type MarketingProductDTO struct {
|
||||||
Id uint `json:"id"`
|
Id uint `json:"id"`
|
||||||
Qty float64 `json:"qty"`
|
MarketingType string `json:"marketing_type"`
|
||||||
UnitPrice float64 `json:"unit_price"`
|
Qty float64 `json:"qty"`
|
||||||
AvgWeight float64 `json:"avg_weight"`
|
UnitPrice float64 `json:"unit_price"`
|
||||||
TotalWeight float64 `json:"total_weight"`
|
AvgWeight float64 `json:"avg_weight"`
|
||||||
TotalPrice float64 `json:"total_price"`
|
TotalWeight float64 `json:"total_weight"`
|
||||||
ProductWarehouse *productWarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
TotalPrice float64 `json:"total_price"`
|
||||||
|
ConvertionUnit *string `json:"convertion_unit,omitempty"`
|
||||||
|
WeightPerConvertion *float64 `json:"weight_per_convertion,omitempty"`
|
||||||
|
TotalPeti *float64 `json:"total_peti,omitempty"`
|
||||||
|
Week *int `json:"week,omitempty"`
|
||||||
|
ProductWarehouse *productWarehouseDTO.ProductWarehousNestedDTO `json:"product_warehouse,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SalesOrdersListDTO struct {
|
type SalesOrdersListDTO struct {
|
||||||
@@ -29,7 +35,7 @@ type SalesOrdersListDTO struct {
|
|||||||
|
|
||||||
// === Mapper Functions ===
|
// === Mapper Functions ===
|
||||||
|
|
||||||
func ToMarketingProductDTO(e entity.MarketingProduct) MarketingProductDTO {
|
func ToMarketingProductDTO(e entity.MarketingProduct, marketingType string) MarketingProductDTO {
|
||||||
var productWarehouse *productWarehouseDTO.ProductWarehousNestedDTO
|
var productWarehouse *productWarehouseDTO.ProductWarehousNestedDTO
|
||||||
|
|
||||||
if e.ProductWarehouse.Id != 0 {
|
if e.ProductWarehouse.Id != 0 {
|
||||||
@@ -37,21 +43,33 @@ func ToMarketingProductDTO(e entity.MarketingProduct) MarketingProductDTO {
|
|||||||
productWarehouse = &mapped
|
productWarehouse = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calculate total_peti only for TELUR marketing type
|
||||||
|
var totalPeti *float64
|
||||||
|
if marketingType == "TELUR" && e.ConvertionUnit != nil && *e.ConvertionUnit == "PETI" && e.WeightPerConvertion != nil && *e.WeightPerConvertion > 0 {
|
||||||
|
calculated := math.Floor(e.TotalWeight / *e.WeightPerConvertion)
|
||||||
|
totalPeti = &calculated
|
||||||
|
}
|
||||||
|
|
||||||
return MarketingProductDTO{
|
return MarketingProductDTO{
|
||||||
Id: e.Id,
|
Id: e.Id,
|
||||||
Qty: e.Qty,
|
MarketingType: marketingType,
|
||||||
UnitPrice: e.UnitPrice,
|
Qty: e.Qty,
|
||||||
AvgWeight: e.AvgWeight,
|
UnitPrice: e.UnitPrice,
|
||||||
TotalWeight: e.TotalWeight,
|
AvgWeight: e.AvgWeight,
|
||||||
TotalPrice: e.TotalPrice,
|
TotalWeight: e.TotalWeight,
|
||||||
ProductWarehouse: productWarehouse,
|
TotalPrice: e.TotalPrice,
|
||||||
|
ConvertionUnit: e.ConvertionUnit,
|
||||||
|
WeightPerConvertion: e.WeightPerConvertion,
|
||||||
|
TotalPeti: totalPeti,
|
||||||
|
Week: e.Week,
|
||||||
|
ProductWarehouse: productWarehouse,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToSalesOrdersListDTO(e entity.Marketing) SalesOrdersListDTO {
|
func ToSalesOrdersListDTO(e entity.Marketing) SalesOrdersListDTO {
|
||||||
products := make([]MarketingProductDTO, len(e.Products))
|
products := make([]MarketingProductDTO, len(e.Products))
|
||||||
for i, p := range e.Products {
|
for i, p := range e.Products {
|
||||||
products[i] = ToMarketingProductDTO(p)
|
products[i] = ToMarketingProductDTO(p, e.MarketingType)
|
||||||
}
|
}
|
||||||
|
|
||||||
return SalesOrdersListDTO{
|
return SalesOrdersListDTO{
|
||||||
@@ -68,7 +86,7 @@ func ToSalesOrdersListDTOFromMarketing(e entity.Marketing) SalesOrdersListDTO {
|
|||||||
if len(e.Products) > 0 {
|
if len(e.Products) > 0 {
|
||||||
salesOrder = make([]MarketingProductDTO, len(e.Products))
|
salesOrder = make([]MarketingProductDTO, len(e.Products))
|
||||||
for i, product := range e.Products {
|
for i, product := range e.Products {
|
||||||
salesOrder[i] = ToMarketingProductDTO(product)
|
salesOrder[i] = ToMarketingProductDTO(product, e.MarketingType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ func (s deliveryOrdersService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("Customer").
|
Preload("Customer").
|
||||||
Preload("SalesPerson").
|
Preload("SalesPerson").
|
||||||
Preload("Products.ProductWarehouse.Product").
|
Preload("Products.ProductWarehouse.Product").
|
||||||
|
Preload("Products.ProductWarehouse.Product.Uom").
|
||||||
Preload("Products.ProductWarehouse.Warehouse").
|
Preload("Products.ProductWarehouse.Warehouse").
|
||||||
Preload("Products.DeliveryProduct")
|
Preload("Products.DeliveryProduct")
|
||||||
}
|
}
|
||||||
@@ -111,6 +112,7 @@ func (s deliveryOrdersService) GetAll(c *fiber.Ctx, params *validation.DeliveryO
|
|||||||
Preload("Customer").
|
Preload("Customer").
|
||||||
Preload("SalesPerson").
|
Preload("SalesPerson").
|
||||||
Preload("Products.ProductWarehouse.Product").
|
Preload("Products.ProductWarehouse.Product").
|
||||||
|
Preload("Products.ProductWarehouse.Product.Uom").
|
||||||
Preload("Products.ProductWarehouse.Warehouse").
|
Preload("Products.ProductWarehouse.Warehouse").
|
||||||
Preload("Products.DeliveryProduct")
|
Preload("Products.DeliveryProduct")
|
||||||
|
|
||||||
@@ -237,6 +239,12 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
||||||
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||||
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
approvalSvcTx := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(dbTransaction))
|
||||||
|
marketingRepoTx := marketingRepo.NewMarketingRepository(dbTransaction)
|
||||||
|
|
||||||
|
marketing, err := marketingRepoTx.GetByID(c.Context(), req.MarketingId, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
||||||
|
}
|
||||||
|
|
||||||
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), req.MarketingId)
|
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), req.MarketingId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -283,25 +291,7 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
itemDeliveryDate = &parsedDate
|
itemDeliveryDate = &parsedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
isPakanOrOVK := false
|
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week)
|
||||||
if foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
|
|
||||||
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
|
|
||||||
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
|
|
||||||
isPakanOrOVK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
|
||||||
var totalPrice float64
|
|
||||||
if isPakanOrOVK {
|
|
||||||
|
|
||||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
|
||||||
} else {
|
|
||||||
|
|
||||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||||
@@ -374,6 +364,12 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
|
|
||||||
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
marketingProductRepositoryTx := marketingRepo.NewMarketingProductRepository(dbTransaction)
|
||||||
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
marketingDeliveryProductRepositoryTx := marketingRepo.NewMarketingDeliveryProductRepository(dbTransaction)
|
||||||
|
marketingRepoTx := marketingRepo.NewMarketingRepository(dbTransaction)
|
||||||
|
|
||||||
|
marketing, err := marketingRepoTx.GetByID(c.Context(), id, nil)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch marketing")
|
||||||
|
}
|
||||||
|
|
||||||
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), id)
|
allMarketingProducts, err := marketingProductRepositoryTx.GetByMarketingID(c.Context(), id)
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
@@ -421,25 +417,7 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
itemDeliveryDate = deliveryProduct.DeliveryDate
|
itemDeliveryDate = deliveryProduct.DeliveryDate
|
||||||
}
|
}
|
||||||
|
|
||||||
isPakanOrOVK := false
|
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketing.MarketingType, requestedProduct.Qty, requestedProduct.AvgWeight, requestedProduct.UnitPrice, foundMarketingProduct.Week)
|
||||||
if foundMarketingProduct.ProductWarehouse.Id != 0 && foundMarketingProduct.ProductWarehouse.Product.Id != 0 && len(foundMarketingProduct.ProductWarehouse.Product.Flags) > 0 {
|
|
||||||
for _, flag := range foundMarketingProduct.ProductWarehouse.Product.Flags {
|
|
||||||
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
|
|
||||||
isPakanOrOVK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
|
||||||
var totalPrice float64
|
|
||||||
if isPakanOrOVK {
|
|
||||||
|
|
||||||
totalPrice = requestedProduct.Qty * requestedProduct.UnitPrice
|
|
||||||
} else {
|
|
||||||
|
|
||||||
totalPrice = totalWeight * requestedProduct.UnitPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||||
@@ -483,6 +461,20 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
return s.getMarketingWithDeliveries(c, id)
|
return s.getMarketingWithDeliveries(c, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *deliveryOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int) (totalWeight, totalPrice float64) {
|
||||||
|
if marketingType == string(utils.MarketingTypeTrading) {
|
||||||
|
totalWeight = 0
|
||||||
|
totalPrice = qty * unitPrice
|
||||||
|
} else if marketingType == string(utils.MarketingTypeAyamPullet) && week != nil && *week > 0 {
|
||||||
|
totalWeight = qty * avgWeight
|
||||||
|
totalPrice = unitPrice * float64(*week) * qty
|
||||||
|
} else {
|
||||||
|
totalWeight = qty * avgWeight
|
||||||
|
totalPrice = totalWeight * unitPrice
|
||||||
|
}
|
||||||
|
return totalWeight, totalPrice
|
||||||
|
}
|
||||||
|
|
||||||
func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64, actorID uint) error {
|
func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gorm.DB, deliveryProduct *entity.MarketingDeliveryProduct, marketingProduct *entity.MarketingProduct, requestedQty float64, actorID uint) error {
|
||||||
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
if marketingProduct == nil || marketingProduct.ProductWarehouseId == 0 {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
return fiber.NewError(fiber.StatusInternalServerError, "Product warehouse not found")
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
@@ -69,6 +70,7 @@ func (s salesOrdersService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("Customer").
|
Preload("Customer").
|
||||||
Preload("SalesPerson").
|
Preload("SalesPerson").
|
||||||
Preload("Products.ProductWarehouse.Product.Flags").
|
Preload("Products.ProductWarehouse.Product.Flags").
|
||||||
|
Preload("Products.ProductWarehouse.Product.Uom").
|
||||||
Preload("Products.ProductWarehouse.Warehouse")
|
Preload("Products.ProductWarehouse.Warehouse")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,6 +105,25 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validasi semua product harus punya marketing_type yang sama
|
||||||
|
if len(req.MarketingProducts) == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "marketing_products is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
firstMarketingType := req.MarketingProducts[0].MarketingType
|
||||||
|
if !utils.IsValidMarketingType(firstMarketingType) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Tipe penjualan tidak valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range req.MarketingProducts {
|
||||||
|
if !utils.IsValidMarketingType(item.MarketingType) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tipe penjualan tidak valid pada produk ke-%d", i+1))
|
||||||
|
}
|
||||||
|
if item.MarketingType != firstMarketingType {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Semua produk harus memiliki tipe penjualan yang sama")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actorID, err := m.ActorIDFromContext(c)
|
actorID, err := m.ActorIDFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -115,6 +136,12 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, item := range req.MarketingProducts {
|
for _, item := range req.MarketingProducts {
|
||||||
|
if item.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Berat rata-rata harus diisi")
|
||||||
|
}
|
||||||
|
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Unit konversi tidak valid")
|
||||||
|
}
|
||||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -149,6 +176,7 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
SoDate: soDate,
|
SoDate: soDate,
|
||||||
SalesPersonId: req.SalesPersonId,
|
SalesPersonId: req.SalesPersonId,
|
||||||
Notes: req.Notes,
|
Notes: req.Notes,
|
||||||
|
MarketingType: firstMarketingType,
|
||||||
CreatedBy: actorID,
|
CreatedBy: actorID,
|
||||||
}
|
}
|
||||||
if err := marketingRepoTx.CreateOne(c.Context(), marketing, nil); err != nil {
|
if err := marketingRepoTx.CreateOne(c.Context(), marketing, nil); err != nil {
|
||||||
@@ -161,10 +189,9 @@ func (s *salesOrdersService) CreateOne(c *fiber.Ctx, req *validation.Create) (*e
|
|||||||
if product.ProductWarehouseId != 0 {
|
if product.ProductWarehouseId != 0 {
|
||||||
pwIDs = append(pwIDs, product.ProductWarehouseId)
|
pwIDs = append(pwIDs, product.ProductWarehouseId)
|
||||||
}
|
}
|
||||||
if err := s.createMarketingProductWithDelivery(c.Context(), marketing.Id, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
if err := s.createMarketingProductWithDelivery(c.Context(), marketing.Id, product.MarketingType, product, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if err := commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(c.Context(), s.MarketingRepo.DB(), pwIDs); err != nil {
|
if err := commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(c.Context(), s.MarketingRepo.DB(), pwIDs); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -207,6 +234,23 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validasi semua product harus punya marketing_type yang sama
|
||||||
|
if len(req.MarketingProducts) > 0 {
|
||||||
|
firstMarketingType := req.MarketingProducts[0].MarketingType
|
||||||
|
if !utils.IsValidMarketingType(firstMarketingType) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Tipe penjualan tidak valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, item := range req.MarketingProducts {
|
||||||
|
if !utils.IsValidMarketingType(item.MarketingType) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Tipe penjualan tidak valid pada produk ke-%d", i+1))
|
||||||
|
}
|
||||||
|
if item.MarketingType != firstMarketingType {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Semua produk harus memiliki tipe penjualan yang sama")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
if err := m.EnsureMarketingAccess(c, s.MarketingRepo.DB(), id); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -234,6 +278,12 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
|
|
||||||
if len(req.MarketingProducts) > 0 {
|
if len(req.MarketingProducts) > 0 {
|
||||||
for _, item := range req.MarketingProducts {
|
for _, item := range req.MarketingProducts {
|
||||||
|
if item.MarketingType != string(utils.MarketingTypeTrading) && item.AvgWeight == 0 {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Berat rata-rata harus diisi")
|
||||||
|
}
|
||||||
|
if item.ConvertionUnit != nil && !utils.IsValidConvertionUnit(*item.ConvertionUnit) {
|
||||||
|
return nil, fiber.NewError(fiber.StatusBadRequest, "Unit konversi tidak valid")
|
||||||
|
}
|
||||||
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
if err := m.EnsureProductWarehouseAccess(c, s.MarketingRepo.DB(), item.ProductWarehouseId); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -281,6 +331,9 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
if req.Notes != "" {
|
if req.Notes != "" {
|
||||||
updateBody["notes"] = req.Notes
|
updateBody["notes"] = req.Notes
|
||||||
}
|
}
|
||||||
|
if len(req.MarketingProducts) > 0 {
|
||||||
|
updateBody["marketing_type"] = req.MarketingProducts[0].MarketingType
|
||||||
|
}
|
||||||
|
|
||||||
if len(updateBody) > 0 {
|
if len(updateBody) > 0 {
|
||||||
if err := marketingRepoTx.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
if err := marketingRepoTx.PatchOne(c.Context(), id, updateBody, nil); err != nil {
|
||||||
@@ -309,38 +362,12 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
for _, rp := range req.MarketingProducts {
|
for _, rp := range req.MarketingProducts {
|
||||||
if old, ok := oldByPW[rp.ProductWarehouseId]; ok {
|
if old, ok := oldByPW[rp.ProductWarehouseId]; ok {
|
||||||
|
|
||||||
// Get product untuk cek flag PAKAN atau OVK
|
totalWeight, totalPrice := s.calculatePriceByMarketingType(rp.MarketingType, rp.Qty, rp.AvgWeight, rp.UnitPrice, rp.Week)
|
||||||
productWarehouse, err := s.ProductWarehouseRepo.GetByID(c.Context(), rp.ProductWarehouseId, func(db *gorm.DB) *gorm.DB {
|
|
||||||
return db.Preload("Product.Flags")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek apakah product punya flag PAKAN atau OVK
|
|
||||||
isPakanOrOVK := false
|
|
||||||
if productWarehouse.Product.Id != 0 && len(productWarehouse.Product.Flags) > 0 {
|
|
||||||
for _, flag := range productWarehouse.Product.Flags {
|
|
||||||
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
|
|
||||||
isPakanOrOVK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
totalWeight := rp.Qty * rp.AvgWeight
|
|
||||||
var totalPrice float64
|
|
||||||
if isPakanOrOVK {
|
|
||||||
totalPrice = rp.Qty * rp.UnitPrice
|
|
||||||
} else {
|
|
||||||
totalPrice = totalWeight * rp.UnitPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check delivery product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check delivery product")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == nil && deliveryProduct.Id != 0 {
|
if err == nil && deliveryProduct.Id != 0 {
|
||||||
oldQty := old.Qty
|
oldQty := old.Qty
|
||||||
newQty := rp.Qty
|
newQty := rp.Qty
|
||||||
@@ -363,12 +390,15 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateBody := map[string]any{
|
updateBody := map[string]any{
|
||||||
"product_warehouse_id": rp.ProductWarehouseId,
|
"product_warehouse_id": rp.ProductWarehouseId,
|
||||||
"qty": rp.Qty,
|
"qty": rp.Qty,
|
||||||
"unit_price": rp.UnitPrice,
|
"unit_price": rp.UnitPrice,
|
||||||
"avg_weight": rp.AvgWeight,
|
"avg_weight": rp.AvgWeight,
|
||||||
"total_weight": totalWeight,
|
"total_weight": totalWeight,
|
||||||
"total_price": totalPrice,
|
"total_price": totalPrice,
|
||||||
|
"convertion_unit": rp.ConvertionUnit,
|
||||||
|
"weight_per_convertion": rp.WeightPerConvertion,
|
||||||
|
"week": rp.Week,
|
||||||
}
|
}
|
||||||
if err := marketingProductRepoTx.PatchOne(c.Context(), old.Id, updateBody, nil); err != nil {
|
if err := marketingProductRepoTx.PatchOne(c.Context(), old.Id, updateBody, nil); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update marketing product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to update marketing product")
|
||||||
@@ -391,7 +421,7 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if err := s.createMarketingProductWithDelivery(c.Context(), id, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
if err := s.createMarketingProductWithDelivery(c.Context(), id, rp.MarketingType, rp, marketingProductRepoTx, invDeliveryRepoTx); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to create marketing product")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -399,7 +429,6 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
|
|
||||||
for _, old := range oldProducts {
|
for _, old := range oldProducts {
|
||||||
if _, ok := reqByPW[old.ProductWarehouseId]; !ok {
|
if _, ok := reqByPW[old.ProductWarehouseId]; !ok {
|
||||||
|
|
||||||
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
deliveryProduct, err := invDeliveryRepoTx.GetByMarketingProductID(c.Context(), old.Id)
|
||||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing delivery product")
|
return fiber.NewError(fiber.StatusInternalServerError, "Failed to check existing delivery product")
|
||||||
@@ -682,45 +711,21 @@ func (s salesOrdersService) Approval(c *fiber.Ctx, req *validation.Approve) ([]e
|
|||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Context, marketingId uint, rp validation.CreateMarketingProduct, marketingProductRepo repository.MarketingProductRepository, invDeliveryRepo repository.MarketingDeliveryProductRepository) error {
|
func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Context, marketingId uint, marketingType string, rp validation.CreateMarketingProduct, marketingProductRepo repository.MarketingProductRepository, invDeliveryRepo repository.MarketingDeliveryProductRepository) error {
|
||||||
|
|
||||||
// Get product untuk cek flag PAKAN atau OVK
|
totalWeight, totalPrice := s.calculatePriceByMarketingType(marketingType, rp.Qty, rp.AvgWeight, rp.UnitPrice, rp.Week)
|
||||||
productWarehouse, err := s.ProductWarehouseRepo.GetByID(ctx, rp.ProductWarehouseId, func(db *gorm.DB) *gorm.DB {
|
|
||||||
return db.Preload("Product.Flags")
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cek apakah product punya flag PAKAN atau OVK
|
|
||||||
isPakanOrOVK := false
|
|
||||||
if productWarehouse.Product.Id != 0 && len(productWarehouse.Product.Flags) > 0 {
|
|
||||||
for _, flag := range productWarehouse.Product.Flags {
|
|
||||||
if flag.Name == string(utils.FlagPakan) || flag.Name == string(utils.FlagOVK) {
|
|
||||||
isPakanOrOVK = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
totalWeight := rp.Qty * rp.AvgWeight
|
|
||||||
var totalPrice float64
|
|
||||||
if isPakanOrOVK {
|
|
||||||
// PAKAN atau OVK: qty × unit_price
|
|
||||||
totalPrice = rp.Qty * rp.UnitPrice
|
|
||||||
} else {
|
|
||||||
// Produk lain: total_weight × unit_price
|
|
||||||
totalPrice = totalWeight * rp.UnitPrice
|
|
||||||
}
|
|
||||||
|
|
||||||
marketingProduct := &entity.MarketingProduct{
|
marketingProduct := &entity.MarketingProduct{
|
||||||
MarketingId: marketingId,
|
MarketingId: marketingId,
|
||||||
ProductWarehouseId: rp.ProductWarehouseId,
|
ProductWarehouseId: rp.ProductWarehouseId,
|
||||||
Qty: rp.Qty,
|
Qty: rp.Qty,
|
||||||
UnitPrice: rp.UnitPrice,
|
UnitPrice: rp.UnitPrice,
|
||||||
AvgWeight: rp.AvgWeight,
|
AvgWeight: rp.AvgWeight,
|
||||||
TotalWeight: totalWeight,
|
TotalWeight: totalWeight,
|
||||||
TotalPrice: totalPrice,
|
TotalPrice: totalPrice,
|
||||||
|
ConvertionUnit: rp.ConvertionUnit,
|
||||||
|
WeightPerConvertion: rp.WeightPerConvertion,
|
||||||
|
Week: rp.Week,
|
||||||
}
|
}
|
||||||
if err := marketingProductRepo.CreateOne(ctx, marketingProduct, nil); err != nil {
|
if err := marketingProductRepo.CreateOne(ctx, marketingProduct, nil); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -744,3 +749,17 @@ func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Cont
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *salesOrdersService) calculatePriceByMarketingType(marketingType string, qty, avgWeight, unitPrice float64, week *int) (totalWeight, totalPrice float64) {
|
||||||
|
if marketingType == string(utils.MarketingTypeTrading) {
|
||||||
|
totalWeight = 0
|
||||||
|
totalPrice = math.Round(qty*unitPrice*100) / 100
|
||||||
|
} else if marketingType == string(utils.MarketingTypeAyamPullet) && week != nil && *week > 0 {
|
||||||
|
totalWeight = math.Round(qty*avgWeight*100) / 100
|
||||||
|
totalPrice = math.Round(unitPrice*float64(*week)*qty*100) / 100
|
||||||
|
} else {
|
||||||
|
totalWeight = math.Round(qty*avgWeight*100) / 100
|
||||||
|
totalPrice = math.Round(totalWeight*unitPrice*100) / 100
|
||||||
|
}
|
||||||
|
return totalWeight, totalPrice
|
||||||
|
}
|
||||||
|
|||||||
@@ -9,11 +9,15 @@ type Create struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CreateMarketingProduct struct {
|
type CreateMarketingProduct struct {
|
||||||
VehicleNumber string `json:"vehicle_number" validate:"required,min=1,max=50"`
|
MarketingType string `json:"marketing_type" validate:"required,min=1,max=50"`
|
||||||
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,gt=0"`
|
VehicleNumber string `json:"vehicle_number" validate:"required,min=1,max=50"`
|
||||||
UnitPrice float64 `json:"unit_price" validate:"required,gt=0"`
|
ConvertionUnit *string `json:"convertion_unit" validate:"omitempty,min=1,max=20"`
|
||||||
Qty float64 `json:"qty" validate:"required,gt=0"`
|
WeightPerConvertion *float64 `json:"weight_per_convertion" validate:"omitempty,gt=0"`
|
||||||
AvgWeight float64 `json:"avg_weight" validate:"required,gt=0"`
|
Week *int `json:"week" validate:"omitempty,gt=0"`
|
||||||
|
ProductWarehouseId uint `json:"product_warehouse_id" validate:"required,gt=0"`
|
||||||
|
UnitPrice float64 `json:"unit_price" validate:"required,gt=0"`
|
||||||
|
Qty float64 `json:"qty" validate:"required,gt=0"`
|
||||||
|
AvgWeight float64 `json:"avg_weight" validate:"omitempty,gt=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Update struct {
|
type Update struct {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
areaRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
fcrRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
|
||||||
flockRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
flockRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/flocks/dto"
|
||||||
kandangRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
kandangRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||||
locationRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
locationRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||||
@@ -13,6 +12,7 @@ import (
|
|||||||
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||||
pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
pfutils "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/utils"
|
||||||
userRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
userRelationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/users/dto"
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// === DTO Structs (ordered) ===
|
// === DTO Structs (ordered) ===
|
||||||
@@ -40,7 +40,7 @@ type ProjectFlockDTO struct {
|
|||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Flock *flockRelationDTO.FlockRelationDTO `json:"flock"`
|
Flock *flockRelationDTO.FlockRelationDTO `json:"flock"`
|
||||||
Area *areaRelationDTO.AreaRelationDTO `json:"area"`
|
Area *areaRelationDTO.AreaRelationDTO `json:"area"`
|
||||||
Fcr *fcrRelationDTO.FcrRelationDTO `json:"fcr"`
|
StandardFcr *float64 `json:"standard_fcr"`
|
||||||
Location *locationRelationDTO.LocationRelationDTO `json:"location"`
|
Location *locationRelationDTO.LocationRelationDTO `json:"location"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,10 +97,6 @@ func ToAreaDTO(e entity.Area) areaRelationDTO.AreaRelationDTO {
|
|||||||
return areaRelationDTO.ToAreaRelationDTO(e)
|
return areaRelationDTO.ToAreaRelationDTO(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
func ToFcrDTO(e entity.Fcr) fcrRelationDTO.FcrRelationDTO {
|
|
||||||
return fcrRelationDTO.ToFcrRelationDTO(e)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ToLocationDTO(e entity.Location) locationRelationDTO.LocationRelationDTO {
|
func ToLocationDTO(e entity.Location) locationRelationDTO.LocationRelationDTO {
|
||||||
return locationRelationDTO.ToLocationRelationDTO(e)
|
return locationRelationDTO.ToLocationRelationDTO(e)
|
||||||
}
|
}
|
||||||
@@ -121,11 +117,6 @@ func ToProjectFlockDTO(pfk entity.ProjectFlockKandang) ProjectFlockDTO {
|
|||||||
mapped := areaRelationDTO.ToAreaRelationDTO(e.Area)
|
mapped := areaRelationDTO.ToAreaRelationDTO(e.Area)
|
||||||
area = &mapped
|
area = &mapped
|
||||||
}
|
}
|
||||||
var fcr *fcrRelationDTO.FcrRelationDTO
|
|
||||||
if e.Fcr.Id != 0 {
|
|
||||||
mapped := fcrRelationDTO.ToFcrRelationDTO(e.Fcr)
|
|
||||||
fcr = &mapped
|
|
||||||
}
|
|
||||||
var location *locationRelationDTO.LocationRelationDTO
|
var location *locationRelationDTO.LocationRelationDTO
|
||||||
if e.Location.Id != 0 {
|
if e.Location.Id != 0 {
|
||||||
mapped := locationRelationDTO.ToLocationRelationDTO(e.Location)
|
mapped := locationRelationDTO.ToLocationRelationDTO(e.Location)
|
||||||
@@ -137,7 +128,7 @@ func ToProjectFlockDTO(pfk entity.ProjectFlockKandang) ProjectFlockDTO {
|
|||||||
Category: e.Category,
|
Category: e.Category,
|
||||||
Flock: flock,
|
Flock: flock,
|
||||||
Area: area,
|
Area: area,
|
||||||
Fcr: fcr,
|
StandardFcr: resolveProjectFlockStandardFcr(e),
|
||||||
Location: location,
|
Location: location,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,6 +213,22 @@ func ToChickinListDTOs(e []entity.ProjectChickin) []ChickinListDTO {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 {
|
||||||
|
if e.ProductionStandard.Id == 0 || len(e.ProductionStandard.ProductionStandardDetails) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
week := 1
|
||||||
|
if e.Category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
week = 18
|
||||||
|
}
|
||||||
|
for _, detail := range e.ProductionStandard.ProductionStandardDetails {
|
||||||
|
if detail.Week == week && detail.StandardFCR != nil {
|
||||||
|
return detail.StandardFCR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ToChickinSimpleDTOs(e []entity.ProjectChickin) []ChickinSimpleDTO {
|
func ToChickinSimpleDTOs(e []entity.ProjectChickin) []ChickinSimpleDTO {
|
||||||
result := make([]ChickinSimpleDTO, len(e))
|
result := make([]ChickinSimpleDTO, len(e))
|
||||||
for i, r := range e {
|
for i, r := range e {
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ func (s chickinService) withRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("ProjectFlockKandang.Kandang.Pic").
|
Preload("ProjectFlockKandang.Kandang.Pic").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock").
|
Preload("ProjectFlockKandang.ProjectFlock").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.Area").
|
Preload("ProjectFlockKandang.ProjectFlock.Area").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.Fcr").
|
Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.Location").
|
Preload("ProjectFlockKandang.ProjectFlock.Location").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.Location.Area")
|
Preload("ProjectFlockKandang.ProjectFlock.Location.Area")
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||||
productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto"
|
productWarehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-warehouses/dto"
|
||||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
|
||||||
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||||
productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"
|
productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"
|
||||||
@@ -31,7 +30,7 @@ type ProjectFlockDTO struct {
|
|||||||
projectFlockDTO.ProjectFlockRelationDTO
|
projectFlockDTO.ProjectFlockRelationDTO
|
||||||
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"`
|
StandardFcr *float64 `json:"standard_fcr,omitempty"`
|
||||||
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
||||||
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
||||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
CreatedUser *userDTO.UserRelationDTO `json:"created_user,omitempty"`
|
||||||
@@ -86,7 +85,7 @@ func toProjectFlockDTO(pf *projectFlockDTO.ProjectFlockListDTO) *ProjectFlockDTO
|
|||||||
ProjectFlockRelationDTO: pf.ProjectFlockRelationDTO,
|
ProjectFlockRelationDTO: pf.ProjectFlockRelationDTO,
|
||||||
Area: pf.Area,
|
Area: pf.Area,
|
||||||
Category: pf.Category,
|
Category: pf.Category,
|
||||||
Fcr: pf.Fcr,
|
StandardFcr: pf.StandardFcr,
|
||||||
ProductionStandard: pf.ProductionStandard,
|
ProductionStandard: pf.ProductionStandard,
|
||||||
Location: pf.Location,
|
Location: pf.Location,
|
||||||
CreatedUser: pf.CreatedUser,
|
CreatedUser: pf.CreatedUser,
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import (
|
|||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
approvalDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/approvals/dto"
|
||||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
|
||||||
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/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"
|
nonstockDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/nonstocks/dto"
|
||||||
@@ -28,7 +27,7 @@ type ProjectFlockListDTO struct {
|
|||||||
ProjectFlockRelationDTO
|
ProjectFlockRelationDTO
|
||||||
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"`
|
StandardFcr *float64 `json:"standard_fcr,omitempty"`
|
||||||
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
||||||
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
||||||
Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"`
|
Kandangs []KandangWithProjectFlockIdDTO `json:"kandangs,omitempty"`
|
||||||
@@ -99,12 +98,6 @@ func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectF
|
|||||||
areaSummary = &mapped
|
areaSummary = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
var fcrSummary *fcrDTO.FcrRelationDTO
|
|
||||||
if e.Fcr.Id != 0 {
|
|
||||||
mapped := fcrDTO.ToFcrRelationDTO(e.Fcr)
|
|
||||||
fcrSummary = &mapped
|
|
||||||
}
|
|
||||||
|
|
||||||
var productionStandardSummary *productionStandardDTO.ProductionStandardRelationDTO
|
var productionStandardSummary *productionStandardDTO.ProductionStandardRelationDTO
|
||||||
if e.ProductionStandard.Id != 0 {
|
if e.ProductionStandard.Id != 0 {
|
||||||
mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProductionStandard)
|
mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProductionStandard)
|
||||||
@@ -129,7 +122,7 @@ func ToProjectFlockListDTOWithPeriod(e entity.ProjectFlock, period int) ProjectF
|
|||||||
Kandangs: kandangSummaries,
|
Kandangs: kandangSummaries,
|
||||||
ProjectBudgets: ToProjectBudgetDTOs(e.Budgets),
|
ProjectBudgets: ToProjectBudgetDTOs(e.Budgets),
|
||||||
Category: e.Category,
|
Category: e.Category,
|
||||||
Fcr: fcrSummary,
|
StandardFcr: resolveProjectFlockStandardFcr(e),
|
||||||
ProductionStandard: productionStandardSummary,
|
ProductionStandard: productionStandardSummary,
|
||||||
Location: locationSummary,
|
Location: locationSummary,
|
||||||
CreatedAt: e.CreatedAt,
|
CreatedAt: e.CreatedAt,
|
||||||
@@ -204,6 +197,22 @@ func createProjectFlockRelationDTO(e entity.ProjectFlock, period int) ProjectFlo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveProjectFlockStandardFcr(e entity.ProjectFlock) *float64 {
|
||||||
|
if e.ProductionStandard.Id == 0 || len(e.ProductionStandard.ProductionStandardDetails) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
week := 1
|
||||||
|
if e.Category == string(utils.ProjectFlockCategoryLaying) {
|
||||||
|
week = 18
|
||||||
|
}
|
||||||
|
for _, detail := range e.ProductionStandard.ProductionStandardDetails {
|
||||||
|
if detail.Week == week && detail.StandardFCR != nil {
|
||||||
|
return detail.StandardFCR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func ToProjectBudgetDTO(e entity.ProjectBudget) ProjectBudgetDTO {
|
func ToProjectBudgetDTO(e entity.ProjectBudget) ProjectBudgetDTO {
|
||||||
var nonstockRef *nonstockDTO.NonstockRelationDTO
|
var nonstockRef *nonstockDTO.NonstockRelationDTO
|
||||||
if e.Nonstock != nil && e.Nonstock.Id != 0 {
|
if e.Nonstock != nil && e.Nonstock.Id != 0 {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
fcrDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/fcrs/dto"
|
|
||||||
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
kandangDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/kandangs/dto"
|
||||||
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
locationDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/locations/dto"
|
||||||
productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"
|
productionStandardDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/dto"
|
||||||
@@ -22,7 +21,7 @@ type ProjectFlockWithPivotDTO struct {
|
|||||||
ProjectFlockRelationDTO
|
ProjectFlockRelationDTO
|
||||||
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
Area *areaDTO.AreaRelationDTO `json:"area,omitempty"`
|
||||||
Category string `json:"category"`
|
Category string `json:"category"`
|
||||||
Fcr *fcrDTO.FcrRelationDTO `json:"fcr,omitempty"`
|
StandardFcr *float64 `json:"standard_fcr,omitempty"`
|
||||||
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
ProductionStandard *productionStandardDTO.ProductionStandardRelationDTO `json:"production_standard,omitempty"`
|
||||||
ProductionStandardId uint `json:"production_standard_id"`
|
ProductionStandardId uint `json:"production_standard_id"`
|
||||||
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
Location *locationDTO.LocationRelationDTO `json:"location,omitempty"`
|
||||||
@@ -67,10 +66,6 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
|
|||||||
mapped := areaDTO.ToAreaRelationDTO(e.ProjectFlock.Area)
|
mapped := areaDTO.ToAreaRelationDTO(e.ProjectFlock.Area)
|
||||||
pfLocal.Area = &mapped
|
pfLocal.Area = &mapped
|
||||||
}
|
}
|
||||||
if e.ProjectFlock.Fcr.Id != 0 {
|
|
||||||
mapped := fcrDTO.ToFcrRelationDTO(e.ProjectFlock.Fcr)
|
|
||||||
pfLocal.Fcr = &mapped
|
|
||||||
}
|
|
||||||
if e.ProjectFlock.ProductionStandard.Id != 0 {
|
if e.ProjectFlock.ProductionStandard.Id != 0 {
|
||||||
mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProjectFlock.ProductionStandard)
|
mapped := productionStandardDTO.ToProductionStandardRelationDTO(e.ProjectFlock.ProductionStandard)
|
||||||
pfLocal.ProductionStandard = &mapped
|
pfLocal.ProductionStandard = &mapped
|
||||||
@@ -83,6 +78,7 @@ func ToProjectFlockKandangDTO(e entity.ProjectFlockKandang) ProjectFlockKandangD
|
|||||||
mapped := userDTO.ToUserRelationDTO(e.ProjectFlock.CreatedUser)
|
mapped := userDTO.ToUserRelationDTO(e.ProjectFlock.CreatedUser)
|
||||||
pfLocal.CreatedUser = &mapped
|
pfLocal.CreatedUser = &mapped
|
||||||
}
|
}
|
||||||
|
pfLocal.StandardFcr = resolveProjectFlockStandardFcr(e.ProjectFlock)
|
||||||
|
|
||||||
for _, k := range e.ProjectFlock.Kandangs {
|
for _, k := range e.ProjectFlock.Kandangs {
|
||||||
kb := kandangDTO.ToKandangRelationDTO(k)
|
kb := kandangDTO.ToKandangRelationDTO(k)
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ type ProjectflockRepository interface {
|
|||||||
GetActiveByLocationID(ctx context.Context, locationID uint64) ([]entity.ProjectFlock, error)
|
GetActiveByLocationID(ctx context.Context, locationID uint64) ([]entity.ProjectFlock, error)
|
||||||
IdExists(ctx context.Context, id uint) (bool, error)
|
IdExists(ctx context.Context, id uint) (bool, error)
|
||||||
AreaExists(ctx context.Context, id uint) (bool, error)
|
AreaExists(ctx context.Context, id uint) (bool, error)
|
||||||
FcrExists(ctx context.Context, id uint) (bool, error)
|
|
||||||
ProductionStandardExists(ctx context.Context, id uint) (bool, error)
|
ProductionStandardExists(ctx context.Context, id uint) (bool, error)
|
||||||
LocationExists(ctx context.Context, id uint) (bool, error)
|
LocationExists(ctx context.Context, id uint) (bool, error)
|
||||||
}
|
}
|
||||||
@@ -67,8 +66,8 @@ func (r *ProjectflockRepositoryImpl) WithDefaultRelations() func(*gorm.DB) *gorm
|
|||||||
return db.
|
return db.
|
||||||
Preload("CreatedUser").
|
Preload("CreatedUser").
|
||||||
Preload("Area").
|
Preload("Area").
|
||||||
Preload("Fcr").
|
|
||||||
Preload("ProductionStandard").
|
Preload("ProductionStandard").
|
||||||
|
Preload("ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("Location").
|
Preload("Location").
|
||||||
Preload("Kandangs").
|
Preload("Kandangs").
|
||||||
Preload("KandangHistory").
|
Preload("KandangHistory").
|
||||||
@@ -134,14 +133,12 @@ func (r *ProjectflockRepositoryImpl) applySearchFilters(db *gorm.DB, rawSearch s
|
|||||||
likeQuery := "%" + normalized + "%"
|
likeQuery := "%" + normalized + "%"
|
||||||
return db.
|
return db.
|
||||||
Joins("LEFT JOIN areas ON areas.id = project_flocks.area_id").
|
Joins("LEFT JOIN areas ON areas.id = project_flocks.area_id").
|
||||||
Joins("LEFT JOIN fcrs ON fcrs.id = project_flocks.fcr_id").
|
|
||||||
Joins("LEFT JOIN production_standards ON production_standards.id = project_flocks.production_standard_id").
|
Joins("LEFT JOIN production_standards ON production_standards.id = project_flocks.production_standard_id").
|
||||||
Joins("LEFT JOIN locations ON locations.id = project_flocks.location_id").
|
Joins("LEFT JOIN locations ON locations.id = project_flocks.location_id").
|
||||||
Joins("LEFT JOIN users AS created_users ON created_users.id = project_flocks.created_by").
|
Joins("LEFT JOIN users AS created_users ON created_users.id = project_flocks.created_by").
|
||||||
Where(`
|
Where(`
|
||||||
LOWER(areas.name) LIKE ?
|
LOWER(areas.name) LIKE ?
|
||||||
OR LOWER(project_flocks.category) LIKE ?
|
OR LOWER(project_flocks.category) LIKE ?
|
||||||
OR LOWER(fcrs.name) LIKE ?
|
|
||||||
OR LOWER(production_standards.name) LIKE ?
|
OR LOWER(production_standards.name) LIKE ?
|
||||||
OR LOWER(locations.name) LIKE ?
|
OR LOWER(locations.name) LIKE ?
|
||||||
OR LOWER(locations.address) LIKE ?
|
OR LOWER(locations.address) LIKE ?
|
||||||
@@ -172,7 +169,6 @@ func (r *ProjectflockRepositoryImpl) applySearchFilters(db *gorm.DB, rawSearch s
|
|||||||
likeQuery,
|
likeQuery,
|
||||||
likeQuery,
|
likeQuery,
|
||||||
likeQuery,
|
likeQuery,
|
||||||
likeQuery,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,10 +180,6 @@ func (r *ProjectflockRepositoryImpl) AreaExists(ctx context.Context, id uint) (b
|
|||||||
return repository.Exists[entity.Area](ctx, r.DB(), id)
|
return repository.Exists[entity.Area](ctx, r.DB(), id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) FcrExists(ctx context.Context, id uint) (bool, error) {
|
|
||||||
return repository.Exists[entity.Fcr](ctx, r.DB(), id)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *ProjectflockRepositoryImpl) ProductionStandardExists(ctx context.Context, id uint) (bool, error) {
|
func (r *ProjectflockRepositoryImpl) ProductionStandardExists(ctx context.Context, id uint) (bool, error) {
|
||||||
return repository.Exists[entity.ProductionStandard](ctx, r.DB(), id)
|
return repository.Exists[entity.ProductionStandard](ctx, r.DB(), id)
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-4
@@ -117,10 +117,10 @@ func (r *projectFlockKandangRepositoryImpl) GetAllWithFilters(ctx context.Contex
|
|||||||
Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
|
Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
|
||||||
Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
|
Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
|
||||||
Preload("ProjectFlock").
|
Preload("ProjectFlock").
|
||||||
Preload("ProjectFlock.Fcr").
|
|
||||||
Preload("ProjectFlock.Area").
|
Preload("ProjectFlock.Area").
|
||||||
Preload("ProjectFlock.Location").
|
Preload("ProjectFlock.Location").
|
||||||
Preload("ProjectFlock.CreatedUser").
|
Preload("ProjectFlock.CreatedUser").
|
||||||
|
Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
@@ -208,10 +208,10 @@ func (r *projectFlockKandangRepositoryImpl) GetAllWithFiltersScoped(ctx context.
|
|||||||
Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
|
Joins("JOIN \"kandangs\" ON \"project_flock_kandangs\".\"kandang_id\" = \"kandangs\".\"id\"").
|
||||||
Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
|
Joins("JOIN \"project_flocks\" ON \"project_flock_kandangs\".\"project_flock_id\" = \"project_flocks\".\"id\"").
|
||||||
Preload("ProjectFlock").
|
Preload("ProjectFlock").
|
||||||
Preload("ProjectFlock.Fcr").
|
|
||||||
Preload("ProjectFlock.Area").
|
Preload("ProjectFlock.Area").
|
||||||
Preload("ProjectFlock.Location").
|
Preload("ProjectFlock.Location").
|
||||||
Preload("ProjectFlock.CreatedUser").
|
Preload("ProjectFlock.CreatedUser").
|
||||||
|
Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
@@ -324,10 +324,10 @@ func (r *projectFlockKandangRepositoryImpl) GetByID(ctx context.Context, id uint
|
|||||||
record := new(entity.ProjectFlockKandang)
|
record := new(entity.ProjectFlockKandang)
|
||||||
if err := r.db.WithContext(ctx).
|
if err := r.db.WithContext(ctx).
|
||||||
Preload("ProjectFlock").
|
Preload("ProjectFlock").
|
||||||
Preload("ProjectFlock.Fcr").
|
|
||||||
Preload("ProjectFlock.Area").
|
Preload("ProjectFlock.Area").
|
||||||
Preload("ProjectFlock.Location").
|
Preload("ProjectFlock.Location").
|
||||||
Preload("ProjectFlock.CreatedUser").
|
Preload("ProjectFlock.CreatedUser").
|
||||||
|
Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
@@ -347,10 +347,10 @@ func (r *projectFlockKandangRepositoryImpl) GetByProjectFlockAndKandang(ctx cont
|
|||||||
if err := r.db.WithContext(ctx).
|
if err := r.db.WithContext(ctx).
|
||||||
Where("project_flock_id = ? AND kandang_id = ?", projectFlockID, kandangID).
|
Where("project_flock_id = ? AND kandang_id = ?", projectFlockID, kandangID).
|
||||||
Preload("ProjectFlock").
|
Preload("ProjectFlock").
|
||||||
Preload("ProjectFlock.Fcr").
|
|
||||||
Preload("ProjectFlock.Area").
|
Preload("ProjectFlock.Area").
|
||||||
Preload("ProjectFlock.Location").
|
Preload("ProjectFlock.Location").
|
||||||
Preload("ProjectFlock.CreatedUser").
|
Preload("ProjectFlock.CreatedUser").
|
||||||
|
Preload("ProjectFlock.ProductionStandard.ProductionStandardDetails").
|
||||||
Preload("ProjectFlock.Kandangs").
|
Preload("ProjectFlock.Kandangs").
|
||||||
Preload("ProjectFlock.KandangHistory").
|
Preload("ProjectFlock.KandangHistory").
|
||||||
Preload("Kandang").
|
Preload("Kandang").
|
||||||
|
|||||||
@@ -282,7 +282,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
|
|
||||||
if err := commonSvc.EnsureRelations(c.Context(),
|
if err := commonSvc.EnsureRelations(c.Context(),
|
||||||
commonSvc.RelationCheck{Name: "Area", ID: &req.AreaId, Exists: s.Repository.AreaExists},
|
commonSvc.RelationCheck{Name: "Area", ID: &req.AreaId, Exists: s.Repository.AreaExists},
|
||||||
commonSvc.RelationCheck{Name: "FCR", ID: &req.FcrId, Exists: s.Repository.FcrExists},
|
|
||||||
commonSvc.RelationCheck{Name: "Production Standard", ID: &req.ProductionStandardId, Exists: s.Repository.ProductionStandardExists},
|
commonSvc.RelationCheck{Name: "Production Standard", ID: &req.ProductionStandardId, Exists: s.Repository.ProductionStandardExists},
|
||||||
commonSvc.RelationCheck{Name: "Location", ID: &req.LocationId, Exists: s.Repository.LocationExists},
|
commonSvc.RelationCheck{Name: "Location", ID: &req.LocationId, Exists: s.Repository.LocationExists},
|
||||||
); err != nil {
|
); err != nil {
|
||||||
@@ -334,7 +333,6 @@ func (s *projectflockService) CreateOne(c *fiber.Ctx, req *validation.Create) (*
|
|||||||
createBody := &entity.ProjectFlock{
|
createBody := &entity.ProjectFlock{
|
||||||
AreaId: req.AreaId,
|
AreaId: req.AreaId,
|
||||||
Category: cat,
|
Category: cat,
|
||||||
FcrId: req.FcrId,
|
|
||||||
ProductionStandardId: req.ProductionStandardId,
|
ProductionStandardId: req.ProductionStandardId,
|
||||||
LocationId: req.LocationId,
|
LocationId: req.LocationId,
|
||||||
CreatedBy: actorID,
|
CreatedBy: actorID,
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ type Create struct {
|
|||||||
FlockName string `json:"flock_name" validate:"required_strict"`
|
FlockName string `json:"flock_name" validate:"required_strict"`
|
||||||
AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"`
|
AreaId uint `json:"area_id" validate:"required_strict,number,gt=0"`
|
||||||
Category string `json:"category" validate:"required_strict"`
|
Category string `json:"category" validate:"required_strict"`
|
||||||
FcrId uint `json:"fcr_id" validate:"required_strict,number,gt=0"`
|
|
||||||
ProductionStandardId uint `json:"production_standard_id" validate:"required_strict,number,gt=0"`
|
ProductionStandardId uint `json:"production_standard_id" validate:"required_strict,number,gt=0"`
|
||||||
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
|
LocationId uint `json:"location_id" validate:"required_strict,number,gt=0"`
|
||||||
KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"`
|
KandangIds []uint `json:"kandang_ids" validate:"required,min=1,dive,gt=0"`
|
||||||
|
|||||||
@@ -280,10 +280,10 @@ func toRecordingProjectFlockDTO(e entity.Recording) RecordingProjectFlockDTO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if pfk.ProjectFlock.Fcr.Id != 0 || e.StandardFcr != nil {
|
if pfk.ProjectFlock.ProductionStandard.Id != 0 || e.StandardFcr != nil {
|
||||||
result.Fcr = &RecordingFcrDTO{
|
result.Fcr = &RecordingFcrDTO{
|
||||||
Id: pfk.ProjectFlock.Fcr.Id,
|
Id: pfk.ProjectFlock.ProductionStandard.Id,
|
||||||
Name: pfk.ProjectFlock.Fcr.Name,
|
Name: pfk.ProjectFlock.ProductionStandard.Name,
|
||||||
FcrStd: floatValue(e.StandardFcr),
|
FcrStd: floatValue(e.StandardFcr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ type RecordingRepository interface {
|
|||||||
GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error)
|
GetFeedUsageInGrams(tx *gorm.DB, recordingID uint) (float64, error)
|
||||||
GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error)
|
GetEggSummaryByRecording(tx *gorm.DB, recordingID uint) (totalQty float64, totalWeightGrams float64, err error)
|
||||||
GetCumulativeEggQtyByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error)
|
GetCumulativeEggQtyByProjectFlockKandang(tx *gorm.DB, projectFlockKandangId uint, recordTime time.Time) (float64, error)
|
||||||
GetFcrStandardNumber(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error)
|
|
||||||
GetTotalWeightProducedFromUniformityByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
GetTotalWeightProducedFromUniformityByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
||||||
GetTotalWeightProducedFromUniformityByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
|
GetTotalWeightProducedFromUniformityByProjectFlockKandangID(ctx context.Context, projectFlockKandangID uint) (float64, error)
|
||||||
GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error)
|
GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error)
|
||||||
@@ -92,7 +91,7 @@ func (r *RecordingRepositoryImpl) WithRelations(db *gorm.DB) *gorm.DB {
|
|||||||
Preload("ProjectFlockKandang.Kandang.Location").
|
Preload("ProjectFlockKandang.Kandang.Location").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock").
|
Preload("ProjectFlockKandang.ProjectFlock").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard").
|
Preload("ProjectFlockKandang.ProjectFlock.ProductionStandard").
|
||||||
Preload("ProjectFlockKandang.ProjectFlock.Fcr").
|
// Preload("ProjectFlockKandang.ProjectFlock.Fcr").
|
||||||
Preload("Depletions").
|
Preload("Depletions").
|
||||||
Preload("Depletions.ProductWarehouse").
|
Preload("Depletions.ProductWarehouse").
|
||||||
Preload("Depletions.ProductWarehouse.Product").
|
Preload("Depletions.ProductWarehouse.Product").
|
||||||
@@ -448,34 +447,6 @@ func (r *RecordingRepositoryImpl) GetCumulativeEggQtyByProjectFlockKandang(
|
|||||||
Scan(&result).Error
|
Scan(&result).Error
|
||||||
return result, err
|
return result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RecordingRepositoryImpl) GetFcrStandardNumber(tx *gorm.DB, fcrId uint, currentWeightKg float64) (float64, bool, error) {
|
|
||||||
if fcrId == 0 || currentWeightKg <= 0 {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var standard entity.FcrStandard
|
|
||||||
err := tx.
|
|
||||||
Where("fcr_id = ? AND weight >= ?", fcrId, currentWeightKg).
|
|
||||||
Order("weight ASC").
|
|
||||||
First(&standard).Error
|
|
||||||
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
err = tx.
|
|
||||||
Where("fcr_id = ?", fcrId).
|
|
||||||
Order("weight DESC").
|
|
||||||
First(&standard).Error
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return 0, false, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return 0, false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return standard.FcrNumber, true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) {
|
func (r *RecordingRepositoryImpl) GetProductionWeightAndQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (totalWeight float64, totalQty float64, err error) {
|
||||||
// Body-weight tracking is removed; keep stub for report compatibility.
|
// Body-weight tracking is removed; keep stub for report compatibility.
|
||||||
return 0, 0, nil
|
return 0, 0, nil
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package dto
|
package dto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -24,12 +25,17 @@ type PurchaseRelationDTO struct {
|
|||||||
|
|
||||||
type PurchaseListDTO struct {
|
type PurchaseListDTO struct {
|
||||||
PurchaseRelationDTO
|
PurchaseRelationDTO
|
||||||
Supplier *supplierDTO.SupplierRelationDTO `json:"supplier"`
|
Supplier *supplierDTO.SupplierRelationDTO `json:"supplier"`
|
||||||
DueDate *time.Time `json:"due_date"`
|
DueDate *time.Time `json:"due_date"`
|
||||||
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
CreatedUser *userDTO.UserRelationDTO `json:"created_user"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
RequesterName string `json:"requester_name"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
PoExpedition []string `json:"po_expedition"`
|
||||||
LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
|
Products []productDTO.ProductRelationDTO `json:"products"`
|
||||||
|
Location *locationDTO.LocationRelationDTO `json:"location"`
|
||||||
|
Area *areaDTO.AreaRelationDTO `json:"area"`
|
||||||
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
LatestApproval *approvalDTO.ApprovalRelationDTO `json:"latest_approval"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PurchaseDetailDTO struct {
|
type PurchaseDetailDTO struct {
|
||||||
@@ -146,6 +152,10 @@ func ToPurchaseListDTO(p entity.Purchase) PurchaseListDTO {
|
|||||||
mapped := userDTO.ToUserRelationDTO(p.CreatedUser)
|
mapped := userDTO.ToUserRelationDTO(p.CreatedUser)
|
||||||
createdUser = &mapped
|
createdUser = &mapped
|
||||||
}
|
}
|
||||||
|
requesterName := ""
|
||||||
|
if createdUser != nil {
|
||||||
|
requesterName = createdUser.Name
|
||||||
|
}
|
||||||
|
|
||||||
var latestApproval *approvalDTO.ApprovalRelationDTO
|
var latestApproval *approvalDTO.ApprovalRelationDTO
|
||||||
if p.LatestApproval != nil && p.LatestApproval.Id != 0 {
|
if p.LatestApproval != nil && p.LatestApproval.Id != 0 {
|
||||||
@@ -153,11 +163,53 @@ func ToPurchaseListDTO(p entity.Purchase) PurchaseListDTO {
|
|||||||
latestApproval = &mapped
|
latestApproval = &mapped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
poExpedition []string
|
||||||
|
location *locationDTO.LocationRelationDTO
|
||||||
|
area *areaDTO.AreaRelationDTO
|
||||||
|
)
|
||||||
|
productMap := make(map[uint]productDTO.ProductRelationDTO)
|
||||||
|
expeditionRefSet := make(map[string]struct{})
|
||||||
|
for i := range p.Items {
|
||||||
|
item := p.Items[i]
|
||||||
|
if item.Product != nil && item.Product.Id != 0 {
|
||||||
|
if _, exists := productMap[item.Product.Id]; !exists {
|
||||||
|
productMap[item.Product.Id] = productDTO.ToProductRelationDTO(*item.Product)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if item.ExpenseNonstock != nil && item.ExpenseNonstock.Expense != nil {
|
||||||
|
ref := strings.TrimSpace(item.ExpenseNonstock.Expense.ReferenceNumber)
|
||||||
|
if ref != "" {
|
||||||
|
if _, exists := expeditionRefSet[ref]; !exists {
|
||||||
|
expeditionRefSet[ref] = struct{}{}
|
||||||
|
poExpedition = append(poExpedition, ref)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if location == nil && item.Warehouse != nil && item.Warehouse.Location != nil && item.Warehouse.Location.Id != 0 {
|
||||||
|
loc := locationDTO.ToLocationRelationDTO(*item.Warehouse.Location)
|
||||||
|
location = &loc
|
||||||
|
}
|
||||||
|
if area == nil && item.Warehouse != nil && item.Warehouse.Area.Id != 0 {
|
||||||
|
ar := areaDTO.ToAreaRelationDTO(item.Warehouse.Area)
|
||||||
|
area = &ar
|
||||||
|
}
|
||||||
|
}
|
||||||
|
products := make([]productDTO.ProductRelationDTO, 0, len(productMap))
|
||||||
|
for _, prod := range productMap {
|
||||||
|
products = append(products, prod)
|
||||||
|
}
|
||||||
|
|
||||||
return PurchaseListDTO{
|
return PurchaseListDTO{
|
||||||
PurchaseRelationDTO: ToPurchaseRelationDTO(&p),
|
PurchaseRelationDTO: ToPurchaseRelationDTO(&p),
|
||||||
Supplier: supplier,
|
Supplier: supplier,
|
||||||
DueDate: p.DueDate,
|
DueDate: p.DueDate,
|
||||||
CreatedUser: createdUser,
|
CreatedUser: createdUser,
|
||||||
|
RequesterName: requesterName,
|
||||||
|
PoExpedition: poExpedition,
|
||||||
|
Products: products,
|
||||||
|
Location: location,
|
||||||
|
Area: area,
|
||||||
CreatedAt: p.CreatedAt,
|
CreatedAt: p.CreatedAt,
|
||||||
UpdatedAt: p.UpdatedAt,
|
UpdatedAt: p.UpdatedAt,
|
||||||
LatestApproval: latestApproval,
|
LatestApproval: latestApproval,
|
||||||
|
|||||||
@@ -31,9 +31,25 @@ func (r *productionResultRepositoryImpl) GetRecordingsByProjectFlockKandang(
|
|||||||
return []entity.Recording{}, 0, nil
|
return []entity.Recording{}, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
latestApproval := r.db.WithContext(ctx).
|
||||||
|
Table("approvals AS a").
|
||||||
|
Select("a.approvable_id, a.action, a.step_number").
|
||||||
|
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.ApprovalWorkflowRecording),
|
||||||
|
)
|
||||||
|
|
||||||
countQuery := r.db.WithContext(ctx).
|
countQuery := r.db.WithContext(ctx).
|
||||||
Model(&entity.Recording{}).
|
Model(&entity.Recording{}).
|
||||||
Where("project_flock_kandangs_id = ?", projectFlockKandangID)
|
Joins("JOIN (?) AS la ON la.approvable_id = recordings.id", latestApproval).
|
||||||
|
Where("project_flock_kandangs_id = ?", projectFlockKandangID).
|
||||||
|
Where("la.step_number = ?", utils.RecordingStepDisetujui).
|
||||||
|
Where("la.action = ?", string(entity.ApprovalActionApproved))
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
if err := countQuery.Count(&total).Error; err != nil {
|
if err := countQuery.Count(&total).Error; err != nil {
|
||||||
@@ -59,7 +75,10 @@ func (r *productionResultRepositoryImpl) GetRecordingsByProjectFlockKandang(
|
|||||||
|
|
||||||
dataQuery := r.db.WithContext(ctx).
|
dataQuery := r.db.WithContext(ctx).
|
||||||
Model(&entity.Recording{}).
|
Model(&entity.Recording{}).
|
||||||
|
Joins("JOIN (?) AS la ON la.approvable_id = recordings.id", latestApproval).
|
||||||
Where("project_flock_kandangs_id = ?", projectFlockKandangID).
|
Where("project_flock_kandangs_id = ?", projectFlockKandangID).
|
||||||
|
Where("la.step_number = ?", utils.RecordingStepDisetujui).
|
||||||
|
Where("la.action = ?", string(entity.ApprovalActionApproved)).
|
||||||
Preload("Eggs", func(db *gorm.DB) *gorm.DB {
|
Preload("Eggs", func(db *gorm.DB) *gorm.DB {
|
||||||
return db.Select("recording_eggs.*, f.name AS product_flag_name").
|
return db.Select("recording_eggs.*, f.name AS product_flag_name").
|
||||||
Joins("LEFT JOIN product_warehouses pw ON pw.id = recording_eggs.product_warehouse_id").
|
Joins("LEFT JOIN product_warehouses pw ON pw.id = recording_eggs.product_warehouse_id").
|
||||||
|
|||||||
@@ -398,6 +398,9 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.
|
|||||||
if detail != nil && detail.TargetMeanBw != nil {
|
if detail != nil && detail.TargetMeanBw != nil {
|
||||||
weeklyResults[i].StdBw = *detail.TargetMeanBw
|
weeklyResults[i].StdBw = *detail.TargetMeanBw
|
||||||
}
|
}
|
||||||
|
if detail != nil {
|
||||||
|
weeklyResults[i].DepStd = valueOrZero(detail.MaxDepletion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1352,10 +1355,12 @@ func buildDebtSupplierRow(purchase entity.Purchase, now time.Time, loc *time.Loc
|
|||||||
poNumber = *purchase.PoNumber
|
poNumber = *purchase.PoNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
prDate := purchase.CreatedAt.In(loc)
|
startDate := resolveDebtSupplierReceivedDate(purchase, loc)
|
||||||
startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc)
|
|
||||||
endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
endDate := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||||
aging := int(endDate.Sub(startDate).Hours() / 24)
|
aging := 0
|
||||||
|
if !startDate.IsZero() {
|
||||||
|
aging = int(endDate.Sub(startDate).Hours() / 24)
|
||||||
|
}
|
||||||
|
|
||||||
totalPrice := 0.0
|
totalPrice := 0.0
|
||||||
travelNumber := "-"
|
travelNumber := "-"
|
||||||
@@ -1525,8 +1530,10 @@ func isDebtSupplierPaid(totalPrice, paymentTotal float64) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func calculateDebtSupplierAging(purchase entity.Purchase, endDate time.Time, loc *time.Location) int {
|
func calculateDebtSupplierAging(purchase entity.Purchase, endDate time.Time, loc *time.Location) int {
|
||||||
prDate := purchase.CreatedAt.In(loc)
|
startDate := resolveDebtSupplierReceivedDate(purchase, loc)
|
||||||
startDate := time.Date(prDate.Year(), prDate.Month(), prDate.Day(), 0, 0, 0, 0, loc)
|
if startDate.IsZero() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
stopDate := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, loc)
|
stopDate := time.Date(endDate.Year(), endDate.Month(), endDate.Day(), 0, 0, 0, 0, loc)
|
||||||
if stopDate.Before(startDate) {
|
if stopDate.Before(startDate) {
|
||||||
return 0
|
return 0
|
||||||
@@ -1534,6 +1541,23 @@ func calculateDebtSupplierAging(purchase entity.Purchase, endDate time.Time, loc
|
|||||||
return int(stopDate.Sub(startDate).Hours() / 24)
|
return int(stopDate.Sub(startDate).Hours() / 24)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resolveDebtSupplierReceivedDate(purchase entity.Purchase, loc *time.Location) time.Time {
|
||||||
|
earliest := time.Time{}
|
||||||
|
for _, item := range purchase.Items {
|
||||||
|
if item.ReceivedDate == nil || item.ReceivedDate.IsZero() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
received := item.ReceivedDate.In(loc)
|
||||||
|
if earliest.IsZero() || received.Before(earliest) {
|
||||||
|
earliest = received
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if earliest.IsZero() {
|
||||||
|
return time.Time{}
|
||||||
|
}
|
||||||
|
return time.Date(earliest.Year(), earliest.Month(), earliest.Day(), 0, 0, 0, 0, loc)
|
||||||
|
}
|
||||||
|
|
||||||
func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error) {
|
func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error) {
|
||||||
params, filters, err := s.parseHppPerKandangQuery(ctx)
|
params, filters, err := s.parseHppPerKandangQuery(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -212,6 +212,31 @@ const (
|
|||||||
KandangStatusActive KandangStatus = "ACTIVE"
|
KandangStatusActive KandangStatus = "ACTIVE"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Marketing Type
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
type MarketingType string
|
||||||
|
|
||||||
|
const (
|
||||||
|
MarketingTypeAyam MarketingType = "AYAM"
|
||||||
|
MarketingTypeTelur MarketingType = "TELUR"
|
||||||
|
MarketingTypeTrading MarketingType = "TRADING"
|
||||||
|
MarketingTypeAyamPullet MarketingType = "AYAM_PULLET"
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
// Convertion Unit
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
type ConvertionUnit string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ConvertionUnitPeti ConvertionUnit = "PETI"
|
||||||
|
ConvertionUnitKG ConvertionUnit = "KG"
|
||||||
|
ConvertionUnitQty ConvertionUnit = "QTY"
|
||||||
|
)
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
// ProjectFlockCategory
|
// ProjectFlockCategory
|
||||||
// -------------------------------------------------------------------
|
// -------------------------------------------------------------------
|
||||||
@@ -329,9 +354,10 @@ const (
|
|||||||
PurchaseStepReceiving approvalutils.ApprovalStep = 4
|
PurchaseStepReceiving approvalutils.ApprovalStep = 4
|
||||||
PurchaseStepCompleted approvalutils.ApprovalStep = 5
|
PurchaseStepCompleted approvalutils.ApprovalStep = 5
|
||||||
|
|
||||||
PurchasePRNumberPrefix = "PR-LTI-"
|
PurchasePRNumberPrefix = "PR-LTI-"
|
||||||
PurchasePONumberPrefix = "PO-LTI-"
|
PurchasePONumberPrefix = "PO-LTI-"
|
||||||
PurchaseNumberPadding = 4
|
AdjustmentStockNumberPrefix = "ADJ-"
|
||||||
|
PurchaseNumberPadding = 4
|
||||||
)
|
)
|
||||||
|
|
||||||
var PurchaseApprovalSteps = map[approvalutils.ApprovalStep]string{
|
var PurchaseApprovalSteps = map[approvalutils.ApprovalStep]string{
|
||||||
@@ -609,6 +635,22 @@ func IsValidPaymentParty(v string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func IsValidMarketingType(v string) bool {
|
||||||
|
switch MarketingType(v) {
|
||||||
|
case MarketingTypeAyam, MarketingTypeTelur, MarketingTypeTrading, MarketingTypeAyamPullet:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsValidConvertionUnit(v string) bool {
|
||||||
|
switch ConvertionUnit(v) {
|
||||||
|
case ConvertionUnitPeti, ConvertionUnitKG, ConvertionUnitQty:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// example use
|
// example use
|
||||||
|
|
||||||
// Recording helper
|
// Recording helper
|
||||||
|
|||||||
Reference in New Issue
Block a user