mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-06-09 15:07:49 +00:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f64839dfe1 | |||
| 0ff720453f |
-17
@@ -1,17 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
-- Revert the TELUR / TELUR_GRADE marketing over-sell block. Removing these rows
|
|
||||||
-- makes resolveOverConsume() fall back to the default allow rule again (the
|
|
||||||
-- post-20260313061525 behaviour). The reasons are unique to this migration, so
|
|
||||||
-- the DELETE only touches rows created here.
|
|
||||||
|
|
||||||
DELETE FROM fifo_stock_v2_overconsume_rules
|
|
||||||
WHERE lane = 'USABLE'
|
|
||||||
AND function_code = 'MARKETING_OUT'
|
|
||||||
AND flag_group_code IN ('TELUR', 'TELUR_GRADE')
|
|
||||||
AND reason IN (
|
|
||||||
'fifo_v2_exception_marketing_block_telur',
|
|
||||||
'fifo_v2_exception_marketing_block_telur_grade'
|
|
||||||
);
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
BEGIN;
|
|
||||||
|
|
||||||
-- Restore the marketing over-sell block for TELUR and TELUR_GRADE only.
|
|
||||||
--
|
|
||||||
-- Migration 20260313061525 narrowed the MARKETING_OUT over-sell block to
|
|
||||||
-- flag_group_code='AYAM' and deactivated the global rule. That left TELUR /
|
|
||||||
-- TELUR_GRADE with no matching block, so resolveOverConsume() fell back to the
|
|
||||||
-- default rule 'fifo_v2_default_allow' (allow_overconsume=TRUE) and egg
|
|
||||||
-- Delivery Orders could over-sell silently into marketing_delivery_products.pending_qty.
|
|
||||||
--
|
|
||||||
-- These rules make resolveOverConsume('TELUR'|'TELUR_GRADE','MARKETING_OUT') = FALSE,
|
|
||||||
-- so an egg DO that exceeds available stock is REJECTED (ErrInsufficientStock)
|
|
||||||
-- instead of being recorded as pending. Scope is "Telur saja" — AYAM and
|
|
||||||
-- transfer behaviour are intentionally left unchanged.
|
|
||||||
--
|
|
||||||
-- NOTE: run the total_used reconciliation (cmd/reconcile-fifo-total-used) BEFORE
|
|
||||||
-- applying this in production. Enabling the block while phantom total_used still
|
|
||||||
-- inflates consumption would reject otherwise-valid egg orders.
|
|
||||||
|
|
||||||
INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active)
|
|
||||||
SELECT 'TELUR', 'MARKETING_OUT', 'USABLE', FALSE, 20, 'fifo_v2_exception_marketing_block_telur', TRUE
|
|
||||||
WHERE NOT EXISTS (
|
|
||||||
SELECT 1 FROM fifo_stock_v2_overconsume_rules
|
|
||||||
WHERE lane = 'USABLE'
|
|
||||||
AND function_code = 'MARKETING_OUT'
|
|
||||||
AND flag_group_code = 'TELUR'
|
|
||||||
AND reason = 'fifo_v2_exception_marketing_block_telur'
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE fifo_stock_v2_overconsume_rules
|
|
||||||
SET allow_overconsume = FALSE, priority = 20, is_active = TRUE
|
|
||||||
WHERE lane = 'USABLE'
|
|
||||||
AND function_code = 'MARKETING_OUT'
|
|
||||||
AND flag_group_code = 'TELUR'
|
|
||||||
AND reason = 'fifo_v2_exception_marketing_block_telur';
|
|
||||||
|
|
||||||
INSERT INTO fifo_stock_v2_overconsume_rules(flag_group_code, function_code, lane, allow_overconsume, priority, reason, is_active)
|
|
||||||
SELECT 'TELUR_GRADE', 'MARKETING_OUT', 'USABLE', FALSE, 20, 'fifo_v2_exception_marketing_block_telur_grade', TRUE
|
|
||||||
WHERE NOT EXISTS (
|
|
||||||
SELECT 1 FROM fifo_stock_v2_overconsume_rules
|
|
||||||
WHERE lane = 'USABLE'
|
|
||||||
AND function_code = 'MARKETING_OUT'
|
|
||||||
AND flag_group_code = 'TELUR_GRADE'
|
|
||||||
AND reason = 'fifo_v2_exception_marketing_block_telur_grade'
|
|
||||||
);
|
|
||||||
|
|
||||||
UPDATE fifo_stock_v2_overconsume_rules
|
|
||||||
SET allow_overconsume = FALSE, priority = 20, is_active = TRUE
|
|
||||||
WHERE lane = 'USABLE'
|
|
||||||
AND function_code = 'MARKETING_OUT'
|
|
||||||
AND flag_group_code = 'TELUR_GRADE'
|
|
||||||
AND reason = 'fifo_v2_exception_marketing_block_telur_grade';
|
|
||||||
|
|
||||||
COMMIT;
|
|
||||||
@@ -964,10 +964,7 @@ func (s deliveryOrdersService) consumeDeliveryStock(ctx context.Context, tx *gor
|
|||||||
marketingProduct.ProductWarehouseId,
|
marketingProduct.ProductWarehouseId,
|
||||||
resolveMarketingAsOf(deliveryProduct.DeliveryDate, deliveryProduct.CreatedAt),
|
resolveMarketingAsOf(deliveryProduct.DeliveryDate, deliveryProduct.CreatedAt),
|
||||||
); err != nil {
|
); err != nil {
|
||||||
if errors.Is(err, fifoV2.ErrInsufficientStock) {
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Insufficient stock for product warehouse %d: %v", marketingProduct.ProductWarehouseId, err))
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Stok tidak mencukupi untuk memenuhi permintaan delivery order ini")
|
|
||||||
}
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("Gagal mengalokasikan stok: %v", err))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshed, err := deliveryProductRepo.GetByID(ctx, deliveryProduct.Id, nil)
|
refreshed, err := deliveryProductRepo.GetByID(ctx, deliveryProduct.Id, nil)
|
||||||
|
|||||||
@@ -172,6 +172,23 @@ func (u *ChickinController) DeleteOne(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *ChickinController) UpdateChickInDate(c *fiber.Ctx) error {
|
||||||
|
req := new(validation.UpdateChickInDate)
|
||||||
|
if err := c.BodyParser(req); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.ChickinService.UpdateChickInDate(c, req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.Status(fiber.StatusOK).JSON(response.Common{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Chick in date berhasil diperbarui",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
func (u *ChickinController) Approval(c *fiber.Ctx) error {
|
||||||
req := new(validation.Approve)
|
req := new(validation.Approve)
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package repository
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
"gitlab.com/mbugroup/lti-api.git/internal/common/repository"
|
||||||
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
entity "gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
@@ -18,6 +19,7 @@ type ProjectChickinRepository interface {
|
|||||||
GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
GetTotalChickinQtyByProjectFlockID(ctx context.Context, projectFlockID uint) (float64, error)
|
||||||
GetByProjectFlockKandangIDForUpdate(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
GetByProjectFlockKandangIDForUpdate(ctx context.Context, projectFlockKandangID uint) ([]entity.ProjectChickin, error)
|
||||||
UpdateUsageFields(ctx context.Context, tx *gorm.DB, chickinID uint, usageQty, pendingUsageQty float64) error
|
UpdateUsageFields(ctx context.Context, tx *gorm.DB, chickinID uint, usageQty, pendingUsageQty float64) error
|
||||||
|
UpdateChickInDateByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, pfkID uint, newDate time.Time) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type ChickinRepositoryImpl struct {
|
type ChickinRepositoryImpl struct {
|
||||||
@@ -134,3 +136,10 @@ func (r *ChickinRepositoryImpl) UpdateUsageFields(ctx context.Context, tx *gorm.
|
|||||||
"pending_usage_qty": pendingUsageQty,
|
"pending_usage_qty": pendingUsageQty,
|
||||||
}).Error
|
}).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *ChickinRepositoryImpl) UpdateChickInDateByProjectFlockKandangID(ctx context.Context, tx *gorm.DB, pfkID uint, newDate time.Time) error {
|
||||||
|
return tx.WithContext(ctx).
|
||||||
|
Model(&entity.ProjectChickin{}).
|
||||||
|
Where("project_flock_kandang_id = ? AND deleted_at IS NULL", pfkID).
|
||||||
|
Update("chick_in_date", newDate).Error
|
||||||
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService
|
|||||||
|
|
||||||
route.Get("/", m.RequirePermissions(m.P_ChickinsGetAll), ctrl.GetAll)
|
route.Get("/", m.RequirePermissions(m.P_ChickinsGetAll), ctrl.GetAll)
|
||||||
route.Post("/", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.CreateOne)
|
route.Post("/", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.CreateOne)
|
||||||
|
route.Patch("/chick-in-date", m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.UpdateChickInDate)
|
||||||
route.Get("/:id", m.RequirePermissions(m.P_ChickinsGetOne), ctrl.GetOne)
|
route.Get("/:id", m.RequirePermissions(m.P_ChickinsGetOne), ctrl.GetOne)
|
||||||
// route.Patch("/:id", ctrl.UpdateOne)
|
// route.Patch("/:id", ctrl.UpdateOne)
|
||||||
route.Delete("/:id", ctrl.DeleteOne)
|
route.Delete("/:id", ctrl.DeleteOne)
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ type ChickinService interface {
|
|||||||
DeleteOne(ctx *fiber.Ctx, id uint) error
|
DeleteOne(ctx *fiber.Ctx, id uint) error
|
||||||
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
Approval(ctx *fiber.Ctx, req *validation.Approve) ([]entity.ProjectChickin, error)
|
||||||
EnsureChickInExists(ctx context.Context, projectFlockKandangID uint) error
|
EnsureChickInExists(ctx context.Context, projectFlockKandangID uint) error
|
||||||
|
UpdateChickInDate(ctx *fiber.Ctx, req *validation.UpdateChickInDate) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type chickinService struct {
|
type chickinService struct {
|
||||||
@@ -2110,3 +2111,38 @@ func (s chickinService) EnsureChickInExists(ctx context.Context, projectFlockKan
|
|||||||
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "Chick in project flock belum disetujui sehingga belum dapat membuat recording")
|
return fiber.NewError(fiber.StatusBadRequest, "Chick in project flock belum disetujui sehingga belum dapat membuat recording")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s chickinService) UpdateChickInDate(ctx *fiber.Ctx, req *validation.UpdateChickInDate) error {
|
||||||
|
if err := s.Validate.Struct(req); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newDate, err := time.Parse("2006-01-02", req.ChickInDate)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "Format tanggal tidak valid, gunakan YYYY-MM-DD")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ProjectflockKandangRepo.GetByID(ctx.Context(), req.ProjectFlockKandangId)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, "Project flock kandang tidak ditemukan")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Repository.DB().WithContext(ctx.Context()).Transaction(func(tx *gorm.DB) error {
|
||||||
|
if err := s.Repository.UpdateChickInDateByProjectFlockKandangID(ctx.Context(), tx, req.ProjectFlockKandangId, newDate); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return tx.Exec(`
|
||||||
|
UPDATE recordings
|
||||||
|
SET day = GREATEST(0, (record_datetime::date - ?::date)::int),
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE project_flock_kandangs_id = ?
|
||||||
|
AND deleted_at IS NULL
|
||||||
|
`, req.ChickInDate, req.ProjectFlockKandangId).Error
|
||||||
|
}); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s.invalidateDepreciationSnapshots(ctx.Context(), nil, []uint{req.ProjectFlockKandangId}, newDate)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
@@ -27,3 +27,8 @@ type Approve struct {
|
|||||||
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
ApprovableIds []uint `json:"approvable_ids" validate:"required_strict,min=1,dive,gt=0"`
|
||||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type UpdateChickInDate struct {
|
||||||
|
ProjectFlockKandangId uint `json:"project_flock_kandang_id" validate:"required,gt=0"`
|
||||||
|
ChickInDate string `json:"chick_in_date" validate:"required,datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user