mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-20 21:41:55 +00:00
Merge branch 'fix/BE/US-278-purchase_adjustment_createOne' into 'development'
Fix/be/us 278 purchase adjustment createone See merge request mbugroup/lti-api!123
This commit is contained in:
@@ -215,21 +215,19 @@ func (s *expenseService) CreateOne(c *fiber.Ctx, req *validation.Create) (*expen
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to get active project flocks for location")
|
||||
}
|
||||
|
||||
if len(activeProjectFlocks) == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "No active project flocks found for this location")
|
||||
}
|
||||
if len(activeProjectFlocks) > 0 {
|
||||
projectFlockIDs := make([]uint64, len(activeProjectFlocks))
|
||||
for i, pf := range activeProjectFlocks {
|
||||
projectFlockIDs[i] = uint64(pf.Id)
|
||||
}
|
||||
|
||||
projectFlockIDs := make([]uint64, len(activeProjectFlocks))
|
||||
for i, pf := range activeProjectFlocks {
|
||||
projectFlockIDs[i] = uint64(pf.Id)
|
||||
projectFlockIdsJSON, err := json.Marshal(projectFlockIDs)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to marshal project_flock_ids")
|
||||
}
|
||||
jsonStr := string(projectFlockIdsJSON)
|
||||
projectFlockIdJSON = &jsonStr
|
||||
}
|
||||
|
||||
projectFlockIdsJSON, err := json.Marshal(projectFlockIDs)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusInternalServerError, "Failed to marshal project_flock_ids")
|
||||
}
|
||||
jsonStr := string(projectFlockIdsJSON)
|
||||
projectFlockIdJSON = &jsonStr
|
||||
}
|
||||
|
||||
expense = &entity.Expense{
|
||||
|
||||
+25
@@ -216,6 +216,31 @@ func (r *ProductWarehouseRepositoryImpl) CleanupEmpty(ctx context.Context, affec
|
||||
return nil
|
||||
}
|
||||
|
||||
var inUseIDs []uint
|
||||
if err := r.DB().WithContext(ctx).
|
||||
Model(&entity.PurchaseItem{}).
|
||||
Where("product_warehouse_id IN ?", emptyIDs).
|
||||
Distinct().
|
||||
Pluck("product_warehouse_id", &inUseIDs).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
if len(inUseIDs) > 0 {
|
||||
inUse := make(map[uint]struct{}, len(inUseIDs))
|
||||
for _, id := range inUseIDs {
|
||||
inUse[id] = struct{}{}
|
||||
}
|
||||
filtered := make([]uint, 0, len(emptyIDs))
|
||||
for _, id := range emptyIDs {
|
||||
if _, exists := inUse[id]; !exists {
|
||||
filtered = append(filtered, id)
|
||||
}
|
||||
}
|
||||
emptyIDs = filtered
|
||||
}
|
||||
if len(emptyIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := r.DB().WithContext(ctx).
|
||||
Model(&entity.PurchaseItem{}).
|
||||
Where("product_warehouse_id IN ?", emptyIDs).
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
@@ -24,13 +25,13 @@ func NewPurchaseController(s service.PurchaseService) *PurchaseController {
|
||||
|
||||
func (ctrl *PurchaseController) GetAll(c *fiber.Ctx) error {
|
||||
query := &validation.Query{
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
CreatedFrom: strings.TrimSpace(c.Query("created_from")),
|
||||
CreatedTo: strings.TrimSpace(c.Query("created_to")),
|
||||
SupplierID: uint(c.QueryInt("supplier_id", 0)),
|
||||
AreaID: uint(c.QueryInt("area_id", 0)),
|
||||
LocationID: uint(c.QueryInt("location_id", 0)),
|
||||
Page: c.QueryInt("page", 1),
|
||||
Limit: c.QueryInt("limit", 10),
|
||||
CreatedFrom: strings.TrimSpace(c.Query("created_from")),
|
||||
CreatedTo: strings.TrimSpace(c.Query("created_to")),
|
||||
SupplierID: uint(c.QueryInt("supplier_id", 0)),
|
||||
AreaID: uint(c.QueryInt("area_id", 0)),
|
||||
LocationID: uint(c.QueryInt("location_id", 0)),
|
||||
ProductCategoryID: uint(c.QueryInt("product_category_id", 0)),
|
||||
}
|
||||
|
||||
@@ -86,7 +87,6 @@ func (ctrl *PurchaseController) CreateOne(c *fiber.Ctx) error {
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
}
|
||||
|
||||
result, err := ctrl.service.CreateOne(c, req)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -161,10 +161,26 @@ func (ctrl *PurchaseController) ReceiveProducts(c *fiber.Ctx) error {
|
||||
}
|
||||
|
||||
req := new(validation.ReceivePurchaseRequest)
|
||||
if err := c.BodyParser(req); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid request body")
|
||||
form, err := c.MultipartForm()
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid multipart form")
|
||||
}
|
||||
req.Action = c.FormValue("action")
|
||||
if notes := strings.TrimSpace(c.FormValue("notes")); notes != "" {
|
||||
req.Notes = ¬es
|
||||
}
|
||||
|
||||
itemsJSON := c.FormValue("items")
|
||||
if strings.TrimSpace(itemsJSON) != "" {
|
||||
if err := json.Unmarshal([]byte(itemsJSON), &req.Items); err != nil {
|
||||
var singleItem validation.ReceivePurchaseItemRequest
|
||||
if err := json.Unmarshal([]byte(itemsJSON), &singleItem); err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Invalid items JSON")
|
||||
}
|
||||
req.Items = []validation.ReceivePurchaseItemRequest{singleItem}
|
||||
}
|
||||
}
|
||||
req.TravelDocuments = form.File["documents"]
|
||||
result, err := ctrl.service.ReceiveProducts(c, uint(id), req)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -98,6 +98,7 @@ func (PurchaseModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate
|
||||
approvalService,
|
||||
expenseBridge,
|
||||
fifoService,
|
||||
documentSvc,
|
||||
)
|
||||
|
||||
userRepo := rUser.NewUserRepository(db)
|
||||
|
||||
@@ -310,9 +310,6 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
return err
|
||||
}
|
||||
if cnt == 1 {
|
||||
if item.Warehouse == nil || item.Warehouse.KandangId == nil || *item.Warehouse.KandangId == 0 {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "Warehouse not connect to kandangs")
|
||||
}
|
||||
newNonstockID, err := b.findExpeditionNonstockID(ctx, supplierID)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -332,7 +329,9 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
"price": pricePerItem,
|
||||
"notes": note,
|
||||
"nonstock_id": newNonstockID,
|
||||
"kandang_id": uint64(*item.Warehouse.KandangId),
|
||||
}
|
||||
if item.Warehouse != nil && item.Warehouse.KandangId != nil && *item.Warehouse.KandangId != 0 {
|
||||
updateBody["kandang_id"] = uint64(*item.Warehouse.KandangId)
|
||||
}
|
||||
if err := b.db.WithContext(ctx).
|
||||
Model(&entity.ExpenseNonstock{}).
|
||||
@@ -395,9 +394,13 @@ func (b *expenseBridge) OnItemsReceived(c *fiber.Ctx, purchaseID uint, updates [
|
||||
}
|
||||
if kandangID != nil {
|
||||
updateBody["kandang_id"] = uint64(*kandangID)
|
||||
} else {
|
||||
updateBody["kandang_id"] = nil
|
||||
}
|
||||
if projectFK != nil {
|
||||
updateBody["project_flock_kandang_id"] = uint64(*projectFK)
|
||||
} else {
|
||||
updateBody["project_flock_kandang_id"] = nil
|
||||
}
|
||||
|
||||
if err := b.db.WithContext(ctx).
|
||||
@@ -550,18 +553,27 @@ func (b *expenseBridge) createExpenseViaService(
|
||||
}
|
||||
|
||||
kandangID := items[0].kandangID
|
||||
if kandangID == nil || *kandangID == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Warehouse not connect to kandangs")
|
||||
}
|
||||
|
||||
kandang, err := b.kandangRepo.GetByID(ctx, *kandangID, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, location_id")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Kandang not found: %d", *kandangID))
|
||||
}
|
||||
if kandang == nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Kandang not found: %d", *kandangID))
|
||||
var locationID uint64
|
||||
var expenseKandangID *uint64
|
||||
if kandangID != nil && *kandangID != 0 {
|
||||
kandang, err := b.kandangRepo.GetByID(ctx, *kandangID, func(db *gorm.DB) *gorm.DB {
|
||||
return db.Select("id, location_id")
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Kandang not found: %d", *kandangID))
|
||||
}
|
||||
if kandang == nil {
|
||||
return nil, fiber.NewError(fiber.StatusNotFound, fmt.Sprintf("Kandang not found: %d", *kandangID))
|
||||
}
|
||||
locationID = uint64(kandang.LocationId)
|
||||
id := uint64(*kandangID)
|
||||
expenseKandangID = &id
|
||||
} else {
|
||||
warehouse := items[0].item.Warehouse
|
||||
if warehouse == nil || warehouse.LocationId == nil || *warehouse.LocationId == 0 {
|
||||
return nil, fiber.NewError(fiber.StatusBadRequest, "Warehouse location is required for expense")
|
||||
}
|
||||
locationID = uint64(*warehouse.LocationId)
|
||||
}
|
||||
|
||||
costItems := make([]expenseValidation.CostItem, 0, len(items))
|
||||
@@ -584,9 +596,9 @@ func (b *expenseBridge) createExpenseViaService(
|
||||
TransactionDate: utils.FormatDate(expenseDate),
|
||||
Category: "BOP",
|
||||
SupplierID: uint64(supplierID),
|
||||
LocationID: uint64(kandang.LocationId),
|
||||
LocationID: locationID,
|
||||
ExpenseNonstocks: []expenseValidation.ExpenseNonstock{{
|
||||
KandangID: func() *uint64 { id := uint64(*kandangID); return &id }(),
|
||||
KandangID: expenseKandangID,
|
||||
CostItems: costItems,
|
||||
}},
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"mime/multipart"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -57,6 +58,7 @@ type purchaseService struct {
|
||||
ApprovalSvc commonSvc.ApprovalService
|
||||
ExpenseBridge PurchaseExpenseBridge
|
||||
FifoSvc commonSvc.FifoService
|
||||
DocumentSvc commonSvc.DocumentService
|
||||
approvalWorkflow approvalutils.ApprovalWorkflowKey
|
||||
}
|
||||
|
||||
@@ -76,6 +78,7 @@ func NewPurchaseService(
|
||||
approvalSvc commonSvc.ApprovalService,
|
||||
expenseBridge PurchaseExpenseBridge,
|
||||
fifoSvc commonSvc.FifoService,
|
||||
documentSvc commonSvc.DocumentService,
|
||||
) PurchaseService {
|
||||
return &purchaseService{
|
||||
Log: utils.Log,
|
||||
@@ -89,6 +92,7 @@ func NewPurchaseService(
|
||||
ApprovalSvc: approvalSvc,
|
||||
ExpenseBridge: expenseBridge,
|
||||
FifoSvc: fifoSvc,
|
||||
DocumentSvc: documentSvc,
|
||||
approvalWorkflow: utils.ApprovalWorkflowPurchase,
|
||||
}
|
||||
}
|
||||
@@ -246,22 +250,25 @@ func (s *purchaseService) CreateOne(c *fiber.Ctx, req *validation.CreatePurchase
|
||||
s.Log.Errorf("Failed to get warehouse %d: %+v", id, err)
|
||||
return nil, nil, utils.Internal("Failed to get warehouse")
|
||||
}
|
||||
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
||||
return nil, nil, utils.BadRequest(fmt.Sprintf("%s is not linked to a kandang", warehouse.Name))
|
||||
}
|
||||
var pfkID *uint
|
||||
if s.ProjectFlockKandangRepo != nil {
|
||||
if pfk, err := s.ProjectFlockKandangRepo.GetActiveByKandangID(c.Context(), uint(*warehouse.KandangId)); err == nil && pfk != nil {
|
||||
if pfk.ClosedAt != nil {
|
||||
return nil, nil, utils.BadRequest("Project sudah closing")
|
||||
isKandang := strings.EqualFold(strings.TrimSpace(warehouse.Type), "KANDANG")
|
||||
if isKandang {
|
||||
if warehouse.KandangId == nil || *warehouse.KandangId == 0 {
|
||||
return nil, nil, utils.BadRequest(fmt.Sprintf("%s is not linked to a kandang", warehouse.Name))
|
||||
}
|
||||
if s.ProjectFlockKandangRepo != nil {
|
||||
if pfk, err := s.ProjectFlockKandangRepo.GetActiveByKandangID(c.Context(), uint(*warehouse.KandangId)); err == nil && pfk != nil {
|
||||
if pfk.ClosedAt != nil {
|
||||
return nil, nil, utils.BadRequest("Project sudah closing")
|
||||
}
|
||||
idCopy := uint(pfk.Id)
|
||||
pfkID = &idCopy
|
||||
} else if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil, utils.BadRequest(fmt.Sprintf("%s has no active project flock", warehouse.Name))
|
||||
} else if err != nil {
|
||||
s.Log.Errorf("Failed to validate project flock for warehouse %d: %+v", id, err)
|
||||
return nil, nil, utils.Internal("Failed to validate project flock")
|
||||
}
|
||||
idCopy := uint(pfk.Id)
|
||||
pfkID = &idCopy
|
||||
} else if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||
return nil, nil, utils.BadRequest(fmt.Sprintf("%s has no active project flock", warehouse.Name))
|
||||
} else if err != nil {
|
||||
s.Log.Errorf("Failed to validate project flock for warehouse %d: %+v", id, err)
|
||||
return nil, nil, utils.Internal("Failed to validate project flock")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,9 +619,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ctx := c.Context()
|
||||
|
||||
action, err := parseApprovalActionInput(req.Action)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -661,6 +666,30 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
if action == entity.ApprovalActionApproved && len(req.TravelDocuments) > 0 {
|
||||
if len(req.TravelDocuments) > len(req.Items) {
|
||||
return nil, utils.BadRequest("Travel documents exceed total receiving items")
|
||||
}
|
||||
for idx, file := range req.TravelDocuments {
|
||||
if file == nil {
|
||||
continue
|
||||
}
|
||||
if idx >= len(req.Items) {
|
||||
break
|
||||
}
|
||||
itemID := req.Items[idx].PurchaseItemID
|
||||
if itemID == 0 {
|
||||
return nil, utils.BadRequest("Purchase item id is required for travel document upload")
|
||||
}
|
||||
uploadedURL, err := s.uploadTravelDocument(ctx, actorID, itemID, file)
|
||||
if err != nil {
|
||||
s.Log.Errorf("Failed to upload travel document for item %d: %+v", itemID, err)
|
||||
return nil, utils.Internal("Failed to upload travel document")
|
||||
}
|
||||
req.Items[idx].TravelDocumentPath = &uploadedURL
|
||||
}
|
||||
}
|
||||
|
||||
itemMap := make(map[uint]*entity.PurchaseItem, len(purchase.Items))
|
||||
for i := range purchase.Items {
|
||||
itemMap[purchase.Items[i].Id] = &purchase.Items[i]
|
||||
@@ -804,32 +833,20 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
for _, prep := range prepared {
|
||||
item := prep.item
|
||||
|
||||
var oldPWID *uint
|
||||
if item.ProductWarehouseId != nil {
|
||||
idCopy := uint(*item.ProductWarehouseId)
|
||||
oldPWID = &idCopy
|
||||
}
|
||||
|
||||
var newPWID *uint
|
||||
clearPW := false
|
||||
|
||||
// Always ensure PW when qty > 0 so stockable has target.
|
||||
if prep.receivedQty > 0 {
|
||||
pwID, err := pwRepoTx.EnsureProductWarehouse(
|
||||
c.Context(),
|
||||
uint(item.ProductId),
|
||||
prep.warehouseID,
|
||||
item.ProjectFlockKandangId,
|
||||
purchase.CreatedBy,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newPWID = &pwID
|
||||
} else if oldPWID != nil {
|
||||
newPWID = oldPWID
|
||||
clearPW = true
|
||||
// Always ensure PW after receiving so linkage stays stable.
|
||||
pwID, err := pwRepoTx.EnsureProductWarehouse(
|
||||
c.Context(),
|
||||
uint(item.ProductId),
|
||||
prep.warehouseID,
|
||||
item.ProjectFlockKandangId,
|
||||
purchase.CreatedBy,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newPWID = &pwID
|
||||
|
||||
deltaQty := prep.receivedQty - item.TotalQty
|
||||
switch {
|
||||
@@ -854,7 +871,7 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
VehicleNumber: prep.payload.VehicleNumber,
|
||||
ReceivedQty: &qtyCopy,
|
||||
ProductWarehouseID: newPWID,
|
||||
ClearProductWarehouse: clearPW,
|
||||
ClearProductWarehouse: false,
|
||||
}
|
||||
|
||||
if prep.overrideWarehouse || uint(item.WarehouseId) != prep.warehouseID {
|
||||
@@ -969,6 +986,38 @@ func (s *purchaseService) ReceiveProducts(c *fiber.Ctx, id uint, req *validation
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func (s *purchaseService) uploadTravelDocument(
|
||||
ctx context.Context,
|
||||
actorID uint,
|
||||
itemID uint,
|
||||
file *multipart.FileHeader,
|
||||
) (string, error) {
|
||||
if file == nil {
|
||||
return "", errors.New("travel document file is required")
|
||||
}
|
||||
if s.DocumentSvc == nil {
|
||||
return "", errors.New("document service not available")
|
||||
}
|
||||
|
||||
documentFiles := []commonSvc.DocumentFile{{
|
||||
File: file,
|
||||
Type: string(utils.DocumentTypePurchaseTravel),
|
||||
}}
|
||||
results, err := s.DocumentSvc.UploadDocuments(ctx, commonSvc.DocumentUploadRequest{
|
||||
DocumentableType: string(utils.DocumentableTypePurchaseItem),
|
||||
DocumentableID: uint64(itemID),
|
||||
CreatedBy: &actorID,
|
||||
Files: documentFiles,
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return "", errors.New("upload result is empty")
|
||||
}
|
||||
return results[0].URL, nil
|
||||
}
|
||||
|
||||
func (s *purchaseService) DeleteItems(c *fiber.Ctx, id uint, req *validation.DeletePurchaseItemsRequest) (*entity.Purchase, error) {
|
||||
if err := s.Validate.Struct(req); err != nil {
|
||||
return nil, err
|
||||
@@ -1048,6 +1097,10 @@ func (s *purchaseService) DeleteItems(c *fiber.Ctx, id uint, req *validation.Del
|
||||
return nil, utils.Internal("Failed to delete purchase items")
|
||||
}
|
||||
|
||||
if err := s.deletePurchaseItemDocuments(ctx, itemsToDelete); err != nil {
|
||||
return nil, utils.Internal("Failed to delete purchase documents")
|
||||
}
|
||||
|
||||
if len(itemsToDelete) > 0 {
|
||||
if err := s.notifyExpenseItemsDeleted(ctx, purchase.Id, itemsToDelete); err != nil {
|
||||
s.Log.Errorf("Failed to sync expense deletion for purchase %d: %+v", purchase.Id, err)
|
||||
@@ -1107,6 +1160,10 @@ func (s *purchaseService) DeletePurchase(c *fiber.Ctx, id uint) error {
|
||||
return utils.Internal("Failed to delete purchase")
|
||||
}
|
||||
|
||||
if err := s.deletePurchaseItemDocuments(ctx, itemsToDelete); err != nil {
|
||||
return utils.Internal("Failed to delete purchase documents")
|
||||
}
|
||||
|
||||
if len(itemsToDelete) > 0 {
|
||||
if err := s.notifyExpenseItemsDeleted(ctx, uint(id), itemsToDelete); err != nil {
|
||||
s.Log.Errorf("Failed to sync expense deletion for purchase %d: %+v", id, err)
|
||||
@@ -1190,6 +1247,21 @@ func (s *purchaseService) notifyExpenseItemsDeleted(ctx context.Context, purchas
|
||||
|
||||
}
|
||||
|
||||
func (s *purchaseService) deletePurchaseItemDocuments(ctx context.Context, items []entity.PurchaseItem) error {
|
||||
if s.DocumentSvc == nil || len(items) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, item := range items {
|
||||
if item.Id == 0 {
|
||||
continue
|
||||
}
|
||||
if err := s.DocumentSvc.DeleteByTarget(ctx, string(utils.DocumentableTypePurchaseItem), uint64(item.Id), true); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *purchaseService) buildStaffAdjustmentPayload(
|
||||
ctx context.Context,
|
||||
purchase *entity.Purchase,
|
||||
@@ -1462,5 +1534,5 @@ func (s *purchaseService) ensureProjectFlockNotClosedForPurchase(
|
||||
return utils.Internal("DB not available for project flock validation")
|
||||
}
|
||||
|
||||
return commonSvc.EnsureProjectFlockNotClosedForProductWarehouses(ctx, db, pfkIDs)
|
||||
return commonSvc.EnsureProjectFlockNotClosedByProjectFlockKandangID(ctx, db, pfkIDs)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package validation
|
||||
|
||||
import "mime/multipart"
|
||||
|
||||
type PurchaseItemPayload struct {
|
||||
WarehouseID uint `json:"warehouse_id" validate:"required,gt=0"`
|
||||
ProductID uint `json:"product_id" validate:"required,gt=0"`
|
||||
@@ -26,7 +28,7 @@ type StaffPurchaseApprovalItem struct {
|
||||
|
||||
type ApproveStaffPurchaseRequest struct {
|
||||
Action string `json:"action" validate:"required,oneof=APPROVED REJECTED"`
|
||||
Items []StaffPurchaseApprovalItem `json:"items,omitempty" validate:"omitempty,min=1,dive"`
|
||||
Items []StaffPurchaseApprovalItem `json:"items" validate:"omitempty,min=1,dive"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
@@ -36,21 +38,22 @@ type ApproveManagerPurchaseRequest struct {
|
||||
}
|
||||
|
||||
type ReceivePurchaseItemRequest struct {
|
||||
PurchaseItemID uint `json:"purchase_item_id" validate:"required,gt=0"`
|
||||
WarehouseID *uint `json:"warehouse_id" validate:"omitempty,gt=0"`
|
||||
ReceivedDate string `json:"received_date" validate:"required,datetime=2006-01-02"`
|
||||
ExpeditionVendorID *uint `json:"expedition_vendor_id,omitempty" validate:"omitempty,gt=0"`
|
||||
TransportPerItem *float64 `json:"transport_per_item,omitempty" validate:"omitempty,gte=0"`
|
||||
TravelNumber *string `json:"travel_number" validate:"omitempty,max=100"`
|
||||
TravelDocumentPath *string `json:"travel_document_path" validate:"omitempty,max=255"`
|
||||
VehicleNumber *string `json:"vehicle_number" validate:"omitempty,max=100"`
|
||||
ReceivedQty *float64 `json:"received_qty" validate:"omitempty,gte=0"`
|
||||
PurchaseItemID uint `form:"purchase_item_id" json:"purchase_item_id" validate:"required,gt=0"`
|
||||
WarehouseID *uint `form:"warehouse_id" json:"warehouse_id" validate:"omitempty,gt=0"`
|
||||
ReceivedDate string `form:"received_date" json:"received_date" validate:"required,datetime=2006-01-02"`
|
||||
ExpeditionVendorID *uint `form:"expedition_vendor_id" json:"expedition_vendor_id,omitempty" validate:"omitempty,gt=0"`
|
||||
TransportPerItem *float64 `form:"transport_per_item" json:"transport_per_item,omitempty" validate:"omitempty,gte=0"`
|
||||
TravelNumber *string `form:"travel_number" json:"travel_number" validate:"omitempty,max=100"`
|
||||
TravelDocumentPath *string `form:"travel_document_path" json:"travel_document_path" validate:"omitempty,max=1024"`
|
||||
VehicleNumber *string `form:"vehicle_number" json:"vehicle_number" validate:"omitempty,max=100"`
|
||||
ReceivedQty *float64 `form:"received_qty" json:"received_qty" validate:"omitempty,gte=0"`
|
||||
}
|
||||
|
||||
type ReceivePurchaseRequest struct {
|
||||
Action string `json:"action" validate:"required,oneof=APPROVED REJECTED"`
|
||||
Items []ReceivePurchaseItemRequest `json:"items,omitempty" validate:"omitempty,min=1,dive"`
|
||||
Notes *string `json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
Action string `form:"action" json:"action" validate:"required,oneof=APPROVED REJECTED"`
|
||||
Items []ReceivePurchaseItemRequest `form:"items" json:"items" validate:"min=1,dive"`
|
||||
TravelDocuments []*multipart.FileHeader `form:"travel_documents" json:"-" validate:"omitempty,dive"`
|
||||
Notes *string `form:"notes" json:"notes,omitempty" validate:"omitempty,max=500"`
|
||||
}
|
||||
|
||||
type DeletePurchaseItemsRequest struct {
|
||||
|
||||
@@ -18,6 +18,6 @@ func RepportRoutes(v1 fiber.Router, u user.UserService, s repport.RepportService
|
||||
route.Get("/expense", m.RequirePermissions(m.P_ReportExpenseGetAll), ctrl.GetExpense)
|
||||
route.Get("/marketing", m.RequirePermissions(m.P_ReportDeliveryGetAll), ctrl.GetMarketing)
|
||||
route.Get("/purchase-supplier", m.RequirePermissions(m.P_ReportPurchaseSupplierGetAll), ctrl.GetPurchaseSupplier)
|
||||
route.Get("/hpp-per-kandang", ctrl.GetHppPerKandang)
|
||||
route.Get("/hpp-per-kandang", m.RequirePermissions(m.P_ReportHppPerKandangGetAll),ctrl.GetHppPerKandang)
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user