mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 13:31:56 +00:00
Merge branch 'development' of https://gitlab.com/mbugroup/lti-api into feat/BE/sso-adjustment
This commit is contained in:
@@ -36,6 +36,7 @@ type ExpenseReceivingPayload struct {
|
||||
TransportPerItem *float64
|
||||
ReceivedQty float64
|
||||
ReceivedDate *time.Time
|
||||
VehicleNumber *string
|
||||
}
|
||||
|
||||
type groupedItem struct {
|
||||
@@ -166,12 +167,21 @@ func (b *expenseBridge) markExpensesUpdated(ctx context.Context, expenseIDs map[
|
||||
if actorID == 0 {
|
||||
actorID = 1
|
||||
}
|
||||
svc := commonSvc.NewApprovalService(commonRepo.NewApprovalRepository(b.db))
|
||||
action := entity.ApprovalActionUpdated
|
||||
approvalRepo := commonRepo.NewApprovalRepository(b.db)
|
||||
svc := commonSvc.NewApprovalService(approvalRepo)
|
||||
action := entity.ApprovalActionCreated
|
||||
|
||||
for id := range expenseIDs {
|
||||
if _, err := svc.CreateApproval(ctx, utils.ApprovalWorkflowExpense, uint(id), utils.ExpenseStepFinance, &action, actorID, nil); err != nil {
|
||||
latestApproval, err := approvalRepo.LatestByTarget(ctx, string(utils.ApprovalWorkflowExpense), uint(id), nil)
|
||||
if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return err
|
||||
}
|
||||
|
||||
if latestApproval == nil {
|
||||
if _, err := svc.CreateApproval(ctx, utils.ApprovalWorkflowExpense, uint(id), utils.ExpenseStepFinance, &action, actorID, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -182,6 +192,22 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
}
|
||||
|
||||
ctx := c.Context()
|
||||
filtered := make([]ExpenseReceivingPayload, 0, len(updates))
|
||||
for _, upd := range updates {
|
||||
if upd.SupplierID == 0 {
|
||||
continue
|
||||
}
|
||||
if upd.TransportPerItem == nil || *upd.TransportPerItem <= 0 {
|
||||
continue
|
||||
}
|
||||
if upd.VehicleNumber == nil || strings.TrimSpace(*upd.VehicleNumber) == "" {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, upd)
|
||||
}
|
||||
if len(filtered) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load current links to decide whether to update in place or recreate.
|
||||
type itemLink struct {
|
||||
@@ -205,9 +231,9 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
|
||||
itemLinks := make(map[uint]itemLink)
|
||||
updatedExpenses := make(map[uint64]struct{})
|
||||
if len(updates) > 0 {
|
||||
ids := make([]uint, 0, len(updates))
|
||||
for _, upd := range updates {
|
||||
if len(filtered) > 0 {
|
||||
ids := make([]uint, 0, len(filtered))
|
||||
for _, upd := range filtered {
|
||||
if upd.PurchaseItemID != 0 {
|
||||
ids = append(ids, upd.PurchaseItemID)
|
||||
}
|
||||
@@ -252,7 +278,7 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
|
||||
groups := make(map[string][]groupedItem)
|
||||
|
||||
for _, payload := range updates {
|
||||
for _, payload := range filtered {
|
||||
if payload.ReceivedDate == nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "received_date is required")
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import (
|
||||
rSupplier "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/repositories"
|
||||
rWarehouse "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/repositories"
|
||||
projectFlockKandangRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/project_flocks/repositories"
|
||||
rStockLogs "gitlab.com/mbugroup/lti-api.git/internal/modules/shared/repositories"
|
||||
rPurchase "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/repositories"
|
||||
validation "gitlab.com/mbugroup/lti-api.git/internal/modules/purchases/validations"
|
||||
"gitlab.com/mbugroup/lti-api.git/internal/utils"
|
||||
@@ -757,6 +758,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
warehouseID uint
|
||||
supplierID uint
|
||||
transportPerItem *float64
|
||||
vehicleNumber *string
|
||||
overrideWarehouse bool
|
||||
receivedQty float64
|
||||
}
|
||||
@@ -805,13 +807,16 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
if receivedQty > item.SubQty {
|
||||
return nil, utils.BadRequest(fmt.Sprintf("Received quantity for item %d cannot exceed ordered quantity (%.3f)", payload.PurchaseItemID, item.SubQty))
|
||||
}
|
||||
if receivedQty < item.TotalUsed {
|
||||
return nil, utils.BadRequest(fmt.Sprintf("Received quantity for item %d cannot be lower than used amount (%.3f)", payload.PurchaseItemID, item.TotalUsed))
|
||||
}
|
||||
|
||||
if _, dup := visitedItems[payload.PurchaseItemID]; dup {
|
||||
return nil, utils.BadRequest(fmt.Sprintf("Duplicate receiving data for item %d", payload.PurchaseItemID))
|
||||
}
|
||||
visitedItems[payload.PurchaseItemID] = struct{}{}
|
||||
|
||||
supplierID := purchase.SupplierId
|
||||
var supplierID uint
|
||||
if payload.ExpeditionVendorID != nil && *payload.ExpeditionVendorID != 0 {
|
||||
supplierID = *payload.ExpeditionVendorID
|
||||
}
|
||||
@@ -825,6 +830,15 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
transportPerItem = &val
|
||||
}
|
||||
|
||||
var vehicleNumber *string
|
||||
if payload.VehicleNumber != nil && strings.TrimSpace(*payload.VehicleNumber) != "" {
|
||||
val := strings.TrimSpace(*payload.VehicleNumber)
|
||||
vehicleNumber = &val
|
||||
} else if item.VehicleNumber != nil && strings.TrimSpace(*item.VehicleNumber) != "" {
|
||||
val := strings.TrimSpace(*item.VehicleNumber)
|
||||
vehicleNumber = &val
|
||||
}
|
||||
|
||||
prepared = append(prepared, preparedReceiving{
|
||||
item: item,
|
||||
payload: payload,
|
||||
@@ -832,6 +846,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
warehouseID: warehouseID,
|
||||
supplierID: supplierID,
|
||||
transportPerItem: transportPerItem,
|
||||
vehicleNumber: vehicleNumber,
|
||||
overrideWarehouse: overrideWarehouse,
|
||||
receivedQty: receivedQty,
|
||||
})
|
||||
@@ -871,19 +886,37 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
receivingAction = entity.ApprovalActionUpdated
|
||||
}
|
||||
}
|
||||
noteSuffix := "receive"
|
||||
if receivingAction == entity.ApprovalActionUpdated {
|
||||
noteSuffix = "edit-receive"
|
||||
}
|
||||
receiveNote := fmt.Sprintf("%s#%s", strings.TrimSpace(*purchase.PoNumber), noteSuffix)
|
||||
|
||||
transactionErr := s.PurchaseRepo.DB().WithContext(c.Context()).Transaction(func(tx *gorm.DB) error {
|
||||
repoTx := rPurchase.NewPurchaseRepository(tx)
|
||||
pwRepoTx := rProductWarehouse.NewProductWarehouseRepository(tx)
|
||||
stockLogRepoTx := rStockLogs.NewStockLogRepository(tx)
|
||||
|
||||
deltas := make(map[uint]float64)
|
||||
affected := make(map[uint]struct{})
|
||||
updates := make([]rPurchase.PurchaseReceivingUpdate, 0, len(prepared))
|
||||
priceUpdates := make([]rPurchase.PurchasePricingUpdate, 0, len(prepared))
|
||||
totalQtyDeltas := make(map[uint]float64)
|
||||
fifoAdds := make([]struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
qty float64
|
||||
}, 0, len(prepared))
|
||||
fifoSubs := make([]struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
qty float64
|
||||
}, 0, len(prepared))
|
||||
logEntries := make([]struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
delta float64
|
||||
}, 0, len(prepared))
|
||||
|
||||
for _, prep := range prepared {
|
||||
item := prep.item
|
||||
@@ -904,16 +937,38 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
newPWID = &pwID
|
||||
|
||||
deltaQty := prep.receivedQty - item.TotalQty
|
||||
switch {
|
||||
case deltaQty > 0 && newPWID != nil:
|
||||
fifoAdds = append(fifoAdds, struct {
|
||||
if newPWID != nil && deltaQty != 0 {
|
||||
logEntries = append(logEntries, struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
qty float64
|
||||
}{itemID: item.Id, pwID: *newPWID, qty: deltaQty})
|
||||
delta float64
|
||||
}{itemID: item.Id, pwID: *newPWID, delta: deltaQty})
|
||||
}
|
||||
switch {
|
||||
case deltaQty > 0 && newPWID != nil:
|
||||
if s.FifoSvc != nil {
|
||||
fifoAdds = append(fifoAdds, struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
qty float64
|
||||
}{itemID: item.Id, pwID: *newPWID, qty: deltaQty})
|
||||
} else {
|
||||
deltas[*newPWID] += deltaQty
|
||||
totalQtyDeltas[item.Id] += deltaQty
|
||||
}
|
||||
case deltaQty < 0 && newPWID != nil:
|
||||
deltas[*newPWID] += deltaQty // negative
|
||||
affected[*newPWID] = struct{}{}
|
||||
if s.FifoSvc != nil {
|
||||
fifoSubs = append(fifoSubs, struct {
|
||||
itemID uint
|
||||
pwID uint
|
||||
qty float64
|
||||
}{itemID: item.Id, pwID: *newPWID, qty: deltaQty})
|
||||
affected[*newPWID] = struct{}{}
|
||||
} else {
|
||||
deltas[*newPWID] += deltaQty // negative
|
||||
affected[*newPWID] = struct{}{}
|
||||
totalQtyDeltas[item.Id] += deltaQty
|
||||
}
|
||||
}
|
||||
|
||||
dateCopy := prep.receivedDate
|
||||
@@ -936,7 +991,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
|
||||
updates = append(updates, update)
|
||||
|
||||
if item.Price > 0 && prep.receivedQty >= 0 {
|
||||
if prep.receivedQty >= 0 {
|
||||
priceUpdates = append(priceUpdates, rPurchase.PurchasePricingUpdate{
|
||||
ItemID: item.Id,
|
||||
Price: item.Price,
|
||||
@@ -953,16 +1008,25 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
return err
|
||||
}
|
||||
|
||||
if err := pwRepoTx.CleanupEmpty(c.Context(), affected); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(priceUpdates) > 0 {
|
||||
if err := repoTx.UpdatePricing(c.Context(), purchase.Id, priceUpdates); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(totalQtyDeltas) > 0 {
|
||||
for itemID, delta := range totalQtyDeltas {
|
||||
if delta == 0 {
|
||||
continue
|
||||
}
|
||||
if err := tx.Model(&entity.PurchaseItem{}).
|
||||
Where("purchase_id = ? AND id = ?", purchase.Id, itemID).
|
||||
Update("total_qty", gorm.Expr("COALESCE(total_qty,0) + ?", delta)).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update due_date based on earliest received date when receiving approved.
|
||||
if earliestReceived != nil {
|
||||
due := earliestReceived.AddDate(0, 0, purchase.CreditTerm)
|
||||
@@ -988,6 +1052,53 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
return err
|
||||
}
|
||||
}
|
||||
for _, adj := range fifoSubs {
|
||||
if adj.pwID == 0 || adj.qty >= 0 {
|
||||
continue
|
||||
}
|
||||
if err := s.FifoSvc.AdjustStockableQuantity(c.Context(), commonSvc.StockAdjustRequest{
|
||||
StockableKey: fifo.StockableKeyPurchaseItems,
|
||||
StockableID: adj.itemID,
|
||||
ProductWarehouseID: adj.pwID,
|
||||
Quantity: adj.qty,
|
||||
Tx: tx,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(logEntries) > 0 {
|
||||
logs := make([]*entity.StockLog, 0, len(logEntries))
|
||||
for _, entry := range logEntries {
|
||||
if entry.pwID == 0 || entry.delta == 0 {
|
||||
continue
|
||||
}
|
||||
log := &entity.StockLog{
|
||||
ProductWarehouseId: entry.pwID,
|
||||
CreatedBy: actorID,
|
||||
LoggableType: string(utils.StockLogTypePurchase),
|
||||
LoggableId: purchase.Id,
|
||||
Notes: receiveNote,
|
||||
}
|
||||
if entry.delta > 0 {
|
||||
log.Increase = entry.delta
|
||||
} else {
|
||||
log.Decrease = -entry.delta
|
||||
}
|
||||
logs = append(logs, log)
|
||||
}
|
||||
if len(logs) > 0 {
|
||||
if err := stockLogRepoTx.CreateMany(c.Context(), logs, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(affected) > 0 {
|
||||
if err := pwRepoTx.CleanupEmpty(c.Context(), affected); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -1019,6 +1130,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
TransportPerItem: prep.transportPerItem,
|
||||
ReceivedQty: prep.receivedQty,
|
||||
ReceivedDate: &date,
|
||||
VehicleNumber: prep.vehicleNumber,
|
||||
}
|
||||
receivingPayloads = append(receivingPayloads, payload)
|
||||
}
|
||||
@@ -1414,10 +1526,6 @@ func (s *purchaseService) buildStaffAdjustmentPayload(
|
||||
qtyCopy := effectiveQty
|
||||
update.Quantity = &qtyCopy
|
||||
}
|
||||
if syncReceiving {
|
||||
qtyCopy := effectiveQty
|
||||
update.TotalQty = &qtyCopy
|
||||
}
|
||||
|
||||
updates = append(updates, update)
|
||||
delete(requestItems, item.Id)
|
||||
|
||||
Reference in New Issue
Block a user