mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
[FEAT/BE] Add restrict purchase chickin
This commit is contained in:
@@ -67,6 +67,7 @@ type PurchaseItemDTO struct {
|
||||
VehicleNumber *string `json:"vehicle_number"`
|
||||
TransportPerItem *float64 `json:"transport_per_item,omitempty"`
|
||||
ExpeditionVendor *supplierDTO.SupplierRelationDTO `json:"expedition_vendor,omitempty"`
|
||||
HasChickin bool `json:"has_chickin"`
|
||||
}
|
||||
|
||||
type PoExpeditionDTO struct {
|
||||
@@ -100,6 +101,7 @@ func ToPurchaseItemDTO(item entity.PurchaseItem) PurchaseItemDTO {
|
||||
TravelNumber: item.TravelNumber,
|
||||
TravelDocumentPath: item.TravelNumberDocs,
|
||||
VehicleNumber: item.VehicleNumber,
|
||||
HasChickin: item.HasChickin,
|
||||
}
|
||||
|
||||
if item.Product != nil && item.Product.Id != 0 {
|
||||
|
||||
@@ -255,6 +255,39 @@ func (s *purchaseService) GetOne(c *fiber.Ctx, id uint) (*entity.Purchase, error
|
||||
if err := s.attachLatestApproval(c.Context(), purchase); err != nil {
|
||||
s.Log.Warnf("Unable to attach latest approval for purchase %d: %+v", id, err)
|
||||
}
|
||||
if len(purchase.Items) > 0 {
|
||||
itemIDs := make([]uint, 0, len(purchase.Items))
|
||||
for i := range purchase.Items {
|
||||
if purchase.Items[i].Id == 0 {
|
||||
continue
|
||||
}
|
||||
itemIDs = append(itemIDs, purchase.Items[i].Id)
|
||||
}
|
||||
if len(itemIDs) > 0 {
|
||||
var usedIDs []uint
|
||||
if err := s.PurchaseRepo.DB().WithContext(c.Context()).
|
||||
Model(&entity.StockAllocation{}).
|
||||
Distinct("stockable_id").
|
||||
Where("stockable_type = ? AND stockable_id IN ? AND usable_type = ? AND status IN ?",
|
||||
fifo.StockableKeyPurchaseItems.String(),
|
||||
itemIDs,
|
||||
fifo.UsableKeyProjectChickin.String(),
|
||||
[]string{entity.StockAllocationStatusActive, entity.StockAllocationStatusPending},
|
||||
).
|
||||
Pluck("stockable_id", &usedIDs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
usedSet := make(map[uint]struct{}, len(usedIDs))
|
||||
for _, id := range usedIDs {
|
||||
usedSet[id] = struct{}{}
|
||||
}
|
||||
for i := range purchase.Items {
|
||||
if _, ok := usedSet[purchase.Items[i].Id]; ok {
|
||||
purchase.Items[i].HasChickin = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
s.applyTravelDocumentURLs(c.Context(), purchase)
|
||||
|
||||
return purchase, nil
|
||||
@@ -498,6 +531,54 @@ func (s *purchaseService) ApproveStaffPurchase(c *fiber.Ctx, id uint, req *valid
|
||||
return nil, utils.BadRequest("Items must not be empty for staff approval")
|
||||
}
|
||||
|
||||
if action == entity.ApprovalActionApproved {
|
||||
itemIDs := make([]uint, 0, len(purchase.Items))
|
||||
itemByID := make(map[uint]entity.PurchaseItem, len(purchase.Items))
|
||||
for i := range purchase.Items {
|
||||
if purchase.Items[i].Id == 0 {
|
||||
continue
|
||||
}
|
||||
itemIDs = append(itemIDs, purchase.Items[i].Id)
|
||||
itemByID[purchase.Items[i].Id] = purchase.Items[i]
|
||||
}
|
||||
if len(itemIDs) > 0 {
|
||||
var usedIDs []uint
|
||||
if err := s.PurchaseRepo.DB().WithContext(ctx).
|
||||
Model(&entity.StockAllocation{}).
|
||||
Distinct("stockable_id").
|
||||
Where("stockable_type = ? AND stockable_id IN ? AND usable_type = ? AND status IN ?",
|
||||
fifo.StockableKeyPurchaseItems.String(),
|
||||
itemIDs,
|
||||
fifo.UsableKeyProjectChickin.String(),
|
||||
[]string{entity.StockAllocationStatusActive, entity.StockAllocationStatusPending},
|
||||
).
|
||||
Pluck("stockable_id", &usedIDs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(usedIDs) > 0 {
|
||||
usedSet := make(map[uint]struct{}, len(usedIDs))
|
||||
for _, id := range usedIDs {
|
||||
usedSet[id] = struct{}{}
|
||||
}
|
||||
for _, payload := range req.Items {
|
||||
if payload.PurchaseItemID == 0 || payload.Qty == nil {
|
||||
continue
|
||||
}
|
||||
if _, used := usedSet[payload.PurchaseItemID]; !used {
|
||||
continue
|
||||
}
|
||||
item, ok := itemByID[payload.PurchaseItemID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if *payload.Qty != item.SubQty {
|
||||
return nil, utils.BadRequest("Purchase sudah chickin, qty tidak bisa diubah")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
payload, err := s.buildStaffAdjustmentPayload(c.Context(), purchase, req, syncReceiving)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -745,6 +826,54 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
req.Items[idx].TravelDocumentPath = &uploadedURL
|
||||
}
|
||||
}
|
||||
if action == entity.ApprovalActionApproved {
|
||||
itemIDs := make([]uint, 0, len(purchase.Items))
|
||||
itemByID := make(map[uint]entity.PurchaseItem, len(purchase.Items))
|
||||
for i := range purchase.Items {
|
||||
if purchase.Items[i].Id == 0 {
|
||||
continue
|
||||
}
|
||||
itemIDs = append(itemIDs, purchase.Items[i].Id)
|
||||
itemByID[purchase.Items[i].Id] = purchase.Items[i]
|
||||
}
|
||||
if len(itemIDs) > 0 {
|
||||
var usedIDs []uint
|
||||
if err := s.PurchaseRepo.DB().WithContext(ctx).
|
||||
Model(&entity.StockAllocation{}).
|
||||
Distinct("stockable_id").
|
||||
Where("stockable_type = ? AND stockable_id IN ? AND usable_type = ? AND status IN ?",
|
||||
fifo.StockableKeyPurchaseItems.String(),
|
||||
itemIDs,
|
||||
fifo.UsableKeyProjectChickin.String(),
|
||||
[]string{entity.StockAllocationStatusActive, entity.StockAllocationStatusPending},
|
||||
).
|
||||
Pluck("stockable_id", &usedIDs).Error; err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(usedIDs) > 0 {
|
||||
usedSet := make(map[uint]struct{}, len(usedIDs))
|
||||
for _, id := range usedIDs {
|
||||
usedSet[id] = struct{}{}
|
||||
}
|
||||
for _, payload := range req.Items {
|
||||
if _, used := usedSet[payload.PurchaseItemID]; !used {
|
||||
continue
|
||||
}
|
||||
item, ok := itemByID[payload.PurchaseItemID]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
receivedQty := item.SubQty
|
||||
if payload.ReceivedQty != nil {
|
||||
receivedQty = *payload.ReceivedQty
|
||||
}
|
||||
if receivedQty != item.TotalQty {
|
||||
return nil, utils.BadRequest("Purchase sudah chickin, qty penerimaan tidak bisa diubah")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
itemMap := make(map[uint]*entity.PurchaseItem, len(purchase.Items))
|
||||
for i := range purchase.Items {
|
||||
@@ -1367,6 +1496,30 @@ func (s *purchaseService) DeletePurchase(c *fiber.Ctx, id uint) error {
|
||||
}
|
||||
|
||||
transactionErr := s.PurchaseRepo.DB().WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
itemIDs := make([]uint, 0, len(itemsToDelete))
|
||||
for _, item := range itemsToDelete {
|
||||
if item.Id == 0 {
|
||||
continue
|
||||
}
|
||||
itemIDs = append(itemIDs, item.Id)
|
||||
}
|
||||
if len(itemIDs) > 0 {
|
||||
var count int64
|
||||
if err := tx.Model(&entity.StockAllocation{}).
|
||||
Where("stockable_type = ? AND stockable_id IN ? AND usable_type = ? AND status IN ?",
|
||||
fifo.StockableKeyPurchaseItems.String(),
|
||||
itemIDs,
|
||||
fifo.UsableKeyProjectChickin.String(),
|
||||
[]string{entity.StockAllocationStatusActive, entity.StockAllocationStatusPending},
|
||||
).
|
||||
Count(&count).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if count > 0 {
|
||||
return utils.BadRequest("Purchase already chickin, failed to delete purchase")
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.rollbackPurchaseStock(ctx, tx, itemsToDelete, note, actorID); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1383,6 +1536,10 @@ func (s *purchaseService) DeletePurchase(c *fiber.Ctx, id uint) error {
|
||||
return nil
|
||||
})
|
||||
if transactionErr != nil {
|
||||
var fe *fiber.Error
|
||||
if errors.As(transactionErr, &fe) {
|
||||
return fe
|
||||
}
|
||||
if errors.Is(transactionErr, gorm.ErrRecordNotFound) {
|
||||
return utils.NotFound("Purchase not found")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user