From e6094528b569bf9600e74f536d2a130e0840fe68 Mon Sep 17 00:00:00 2001 From: ragilap Date: Mon, 8 Dec 2025 17:30:11 +0700 Subject: [PATCH 01/15] add project flock middleware --- internal/middleware/auth.go | 73 ++++++++++++++- internal/middleware/permissions.go | 91 +++++-------------- .../project-flock-kandangs/route.go | 4 +- .../production/project_flocks/route.go | 18 ++-- internal/modules/users/route.go | 8 +- 5 files changed, 106 insertions(+), 88 deletions(-) diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index 881c3a67..cf5ce1f3 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -3,14 +3,13 @@ package middleware import ( "strings" - "gitlab.com/mbugroup/lti-api.git/internal/config" + "github.com/gofiber/fiber/v2" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" "gitlab.com/mbugroup/lti-api.git/internal/modules/sso/session" service "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" "gitlab.com/mbugroup/lti-api.git/internal/sso" "gitlab.com/mbugroup/lti-api.git/internal/utils" - - "github.com/gofiber/fiber/v2" + "gitlab.com/mbugroup/lti-api.git/internal/config" ) const ( @@ -199,3 +198,71 @@ func hasAllScopes(have, required []string) bool { } return true } + +// RequirePermissions ensures the authenticated user possesses all specified permissions. +func RequirePermissions(perms ...string) fiber.Handler { + required := canonicalPermissions(perms) + return func(c *fiber.Ctx) error { + if len(required) == 0 { + return c.Next() + } + + ctx, ok := AuthDetails(c) + if !ok || ctx == nil { + return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate") + } + + userPerms := ctx.permissionSet() + if len(userPerms) == 0 { + return fiber.NewError(fiber.StatusForbidden, "Insufficient permission") + } + + for _, perm := range required { + if _, has := userPerms[perm]; !has { + return fiber.NewError(fiber.StatusForbidden, "Insufficient permission") + } + } + + return c.Next() + } +} + +// HasPermission reports whether the current request context includes the given permission. +func HasPermission(c *fiber.Ctx, perm string) bool { + ctx, ok := AuthDetails(c) + if !ok || ctx == nil { + return false + } + perm = canonicalPermission(perm) + if perm == "" { + return false + } + _, has := ctx.permissionSet()[perm] + return has +} + +func (a *AuthContext) permissionSet() map[string]struct{} { + if a == nil || a.Permissions == nil { + return nil + } + return a.Permissions +} + +func canonicalPermissions(perms []string) []string { + out := make([]string, 0, len(perms)) + seen := make(map[string]struct{}, len(perms)) + for _, perm := range perms { + if canonical := canonicalPermission(perm); canonical != "" { + if _, ok := seen[canonical]; ok { + continue + } + seen[canonical] = struct{}{} + out = append(out, canonical) + } + } + return out +} + +func canonicalPermission(perm string) string { + return strings.ToLower(strings.TrimSpace(perm)) +} \ No newline at end of file diff --git a/internal/middleware/permissions.go b/internal/middleware/permissions.go index 3ebe6866..37e26b47 100644 --- a/internal/middleware/permissions.go +++ b/internal/middleware/permissions.go @@ -1,75 +1,26 @@ package middleware -import ( - "strings" +//project-flock +const ( + P_ProjectFlockKandangsClosing = "lti.production.project_flock_kandangs.closing" + P_ProjectFlockKandangsGetAll = "lti.production.project_flock_kandangs.list" + P_ProjectFlockKandangsGetOne = "lti.production.project_flock_kandangs.detail" - "github.com/gofiber/fiber/v2" + P_ProjectFlockGetAll = "lti.production.project_flocks.list" + P_ProjectFlockCreate = "lti.production.project_flocks.create" + P_ProjectFlockGetOne = "lti.production.project_flocks.detail" + P_ProjectFlockUpdate = "lti.production.project_flocks.update" + P_ProjectFlockDelete = "lti.production.project_flocks.delete" + P_ProjectFlockApprove = "lti.production.project_flocks.approve" + P_ProjectFlockLookup = "lti.production.project_flocks.lookup" + P_ProjectFlockNextPeriod = "lti.production.project_flocks.next_period" + P_ProjectFlockResubmit = "lti.production.project_flocks.resubmit" ) -// RequirePermissions ensures the authenticated user possesses all specified permissions. -func RequirePermissions(perms ...string) fiber.Handler { - required := canonicalPermissions(perms) - return func(c *fiber.Ctx) error { - if len(required) == 0 { - return c.Next() - } - - ctx, ok := AuthDetails(c) - if !ok || ctx == nil { - return fiber.NewError(fiber.StatusUnauthorized, "Please authenticate") - } - - userPerms := ctx.permissionSet() - if len(userPerms) == 0 { - return fiber.NewError(fiber.StatusForbidden, "Insufficient permission") - } - - for _, perm := range required { - if _, has := userPerms[perm]; !has { - return fiber.NewError(fiber.StatusForbidden, "Insufficient permission") - } - } - - return c.Next() - } -} - -// HasPermission reports whether the current request context includes the given permission. -func HasPermission(c *fiber.Ctx, perm string) bool { - ctx, ok := AuthDetails(c) - if !ok || ctx == nil { - return false - } - perm = canonicalPermission(perm) - if perm == "" { - return false - } - _, has := ctx.permissionSet()[perm] - return has -} - -func (a *AuthContext) permissionSet() map[string]struct{} { - if a == nil || a.Permissions == nil { - return nil - } - return a.Permissions -} - -func canonicalPermissions(perms []string) []string { - out := make([]string, 0, len(perms)) - seen := make(map[string]struct{}, len(perms)) - for _, perm := range perms { - if canonical := canonicalPermission(perm); canonical != "" { - if _, ok := seen[canonical]; ok { - continue - } - seen[canonical] = struct{}{} - out = append(out, canonical) - } - } - return out -} - -func canonicalPermission(perm string) string { - return strings.ToLower(strings.TrimSpace(perm)) -} +//recording +const ( + PermissionRecordingRead = "recording.index" + PermissionRecordingCreate = "recording.create" + PermissionRecordingUpdate = "recording.update" + PermissionRecordingDelete = "recording.delete" +) \ No newline at end of file diff --git a/internal/modules/production/project-flock-kandangs/route.go b/internal/modules/production/project-flock-kandangs/route.go index 7bab770e..d4dfec30 100644 --- a/internal/modules/production/project-flock-kandangs/route.go +++ b/internal/modules/production/project-flock-kandangs/route.go @@ -20,7 +20,7 @@ func ProjectFlockKandangRoutes(v1 fiber.Router, u user.UserService, s projectFlo // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/", ctrl.GetAll) - route.Get("/:id", ctrl.GetOne) + route.Get("/",m.RequirePermissions(m.P_ProjectFlockKandangsGetAll), ctrl.GetAll) + route.Get("/:id",m.RequirePermissions(m.P_ProjectFlockKandangsGetOne), ctrl.GetOne) } diff --git a/internal/modules/production/project_flocks/route.go b/internal/modules/production/project_flocks/route.go index 710f5225..a962fd56 100644 --- a/internal/modules/production/project_flocks/route.go +++ b/internal/modules/production/project_flocks/route.go @@ -15,14 +15,14 @@ func ProjectflockRoutes(v1 fiber.Router, u user.UserService, s projectflock.Proj route := v1.Group("/project-flocks") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) - route.Get("/kandangs/lookup", ctrl.LookupProjectFlockKandang) - route.Post("/approvals", ctrl.Approval) - route.Get("/locations/:location_id/periods", ctrl.GetPeriodSummary) - route.Put("/:id/resubmit", ctrl.Resubmit) + route.Get("/",m.RequirePermissions(m.P_ProjectFlockGetAll),ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_ProjectFlockCreate), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_ProjectFlockGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_ProjectFlockUpdate), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_ProjectFlockGetAll), ctrl.DeleteOne) + route.Get("/kandangs/lookup",m.RequirePermissions(m.P_ProjectFlockLookup), ctrl.LookupProjectFlockKandang) + route.Post("/approvals",m.RequirePermissions(m.P_ProjectFlockApprove), ctrl.Approval) + route.Get("/locations/:location_id/periods",m.RequirePermissions(m.P_ProjectFlockNextPeriod), ctrl.GetPeriodSummary) + route.Put("/:id/resubmit",m.RequirePermissions(m.P_ProjectFlockResubmit), ctrl.Resubmit) } diff --git a/internal/modules/users/route.go b/internal/modules/users/route.go index 9ba6bfb3..1093312f 100644 --- a/internal/modules/users/route.go +++ b/internal/modules/users/route.go @@ -3,7 +3,7 @@ package users import ( "github.com/gofiber/fiber/v2" - "gitlab.com/mbugroup/lti-api.git/internal/middleware" + m "gitlab.com/mbugroup/lti-api.git/internal/middleware" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/users/controllers" user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" ) @@ -12,11 +12,11 @@ func UserRoutes(v1 fiber.Router, s user.UserService) { ctrl := controller.NewUserController(s) route := v1.Group("/users") - route.Use(middleware.Auth(s)) + route.Use(m.Auth(s)) - route.Get("/", ctrl.GetAll) + route.Get("/", m.RequirePermissions("lti.users.list"), ctrl.GetAll) // route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) + route.Get("/:id", m.RequirePermissions("lti.users.detail"), ctrl.GetOne) // route.Patch("/:id", ctrl.UpdateOne) // route.Delete("/:id", ctrl.DeleteOne) } From 2effa0864880504dd1c69a8cd662757fba455bd2 Mon Sep 17 00:00:00 2001 From: ragilap Date: Wed, 10 Dec 2025 08:53:09 +0700 Subject: [PATCH 02/15] feat/BE/US-304/TASK-307,306-adjustment middleware check if user have permission,create all permission in modules lti --- internal/middleware/permissions.go | 165 +++++++++++++++++- internal/modules/approvals/route.go | 2 +- internal/modules/closings/route.go | 10 +- internal/modules/constants/route.go | 1 - internal/modules/expenses/route.go | 24 +-- .../modules/inventory/adjustments/route.go | 10 +- .../modules/inventory/product-stocks/route.go | 8 +- .../inventory/product-warehouses/route.go | 4 +- internal/modules/inventory/transfers/route.go | 6 +- .../marketing/delivery-orderss/route.go | 11 +- .../modules/marketing/sales-orders/route.go | 11 +- internal/modules/master/areas/route.go | 10 +- internal/modules/master/banks/route.go | 11 +- internal/modules/master/customers/route.go | 10 +- internal/modules/master/fcrs/route.go | 10 +- internal/modules/master/flocks/route.go | 10 +- internal/modules/master/kandangs/route.go | 10 +- internal/modules/master/locations/route.go | 10 +- internal/modules/master/nonstocks/route.go | 10 +- .../master/product-categories/route.go | 10 +- internal/modules/master/products/route.go | 10 +- internal/modules/master/suppliers/route.go | 10 +- internal/modules/master/uoms/route.go | 6 + internal/modules/master/warehouses/route.go | 10 +- internal/modules/production/chickins/route.go | 6 +- .../project-flock-kandangs/route.go | 6 - .../modules/production/recordings/route.go | 14 +- internal/modules/purchases/route.go | 16 +- internal/modules/users/route.go | 4 +- 29 files changed, 289 insertions(+), 136 deletions(-) diff --git a/internal/middleware/permissions.go b/internal/middleware/permissions.go index 37e26b47..0734b035 100644 --- a/internal/middleware/permissions.go +++ b/internal/middleware/permissions.go @@ -17,10 +17,167 @@ const ( P_ProjectFlockResubmit = "lti.production.project_flocks.resubmit" ) +const( + P_ExpenseGetAll= "lti.expense.list" + P_ExpenseCreateOne= "lti.expense.create" + P_ExpenseUpdateOne= "lti.expense.update" + P_ExpenseGetOne= "lti.expense.detail" + P_ExpenseDeleteOne= "lti.expense.delete" + P_ExpenseApprovalManager= "lti.expense.approve.manager" + P_ExpenseApprovalFinance= "lti.expense.approve.finance" + P_ExpenseCreateRealizations= "lti.expense.create.realization" + P_ExpenseUpdateRealizations= "lti.expense.update.realization" + P_ExpenseCompleteExpense= "lti.expense.complete.expense" + P_ExpenseDocument= "lti.expense.document" + P_ExpenseDocumentRealizations= "lti.expense.document.realization" +) +const( + P_AdjustmentGetAll="lti.inventory.list" + P_AdjustmentCreate="lti.inventory.create" + P_AdjustmentGetOne="lti.inventory.detail" +) +const( + P_ApprovalGetAll = "lti.approval.list" +) + +const( + P_ClosingGetAll = "lti.closing.list" + P_ClosingPenjualan = "lti.closing.penjualan" + P_ClosingGetSummary = "lti.closing.getsummary" + P_ProductStockGetAll = "lti.inventory.product_stock.list" + P_ProductStockGetOne = "lti.inventory.product_stock.detail" + P_ProductWarehousekGetAll = "lti.inventory.product_warehouses.list" + P_ProductWarehouseGetOne = "lti.inventory.product_warehouses.detail" +) + +const( + P_TransferGetAll = "lti.inventory.transfer.list" + P_TransferGetOne = "lti.inventory.transfer.detail" + P_TransferCreateOne = "lti.inventory.transfer.create" +) + +const( + P_DeliveryGetAll = "lti.marketing.delivery_order.list" + P_DeliveryGetOne = "lti.marketing.delivery_order.detail" + P_DeliveryCreateOne = "lti.marketing.delivery_order.create" + P_DeliveryUpdateOne = "lti.marketing.delivery_order.update" + P_SalesOrderDelete = "lti.marketing.sales_order.delete" + P_SalesOrderApproval = "lti.marketing.sales_order.approve" + P_SalesOrderCreateOne = "lti.marketing.sales_order.create" + P_SalesOrderUpdateOne = "lti.marketing.sales_order.update" +) + +const( + P_AreaGetAll = "lti.master.area.list" + P_AreaGetOne = "lti.master.area.detail" + P_AreaCreateOne = "lti.master.area.create" + P_AreaUpdateOne = "lti.master.area.update" + P_AreaDeleteOne = "lti.master.area.delete" + + P_BanksGetAll = "lti.master.banks.list" + P_BanksGetOne = "lti.master.banks.detail" + P_BanksCreateOne = "lti.master.banks.create" + P_BanksUpdateOne = "lti.master.banks.update" + P_BanksDeleteOne = "lti.master.banks.delete" + + P_CustomerGetAll = "lti.master.customer.list" + P_CustomerGetOne = "lti.master.customer.detail" + P_CustomerCreateOne = "lti.master.customer.create" + P_CustomerUpdateOne = "lti.master.customer.update" + P_CustomerDeleteOne = "lti.master.customer.delete" + + P_FcrGetAll = "lti.master.fcr.list" + P_FcrGetOne = "lti.master.fcr.detail" + P_FcrCreateOne = "lti.master.fcr.create" + P_FcrUpdateOne = "lti.master.fcr.update" + P_FcrDeleteOne = "lti.master.fcr.delete" + + P_FlocksGetAll = "lti.master.flocks.list" + P_FlocksGetOne = "lti.master.flocks.detail" + P_FlocksCreateOne = "lti.master.flocks.create" + P_FlocksUpdateOne = "lti.master.flocks.update" + P_FlocksDeleteOne = "lti.master.flocks.delete" + + P_KandangsGetAll = "lti.master.kandangs.list" + P_KandangsGetOne = "lti.master.kandangs.detail" + P_KandangsCreateOne = "lti.master.kandangs.create" + P_KandangsUpdateOne = "lti.master.kandangs.update" + P_KandangsDeleteOne = "lti.master.kandangs.delete" + + P_LocationsGetAll = "lti.master.locations.list" + P_LocationsGetOne = "lti.master.locations.detail" + P_LocationsCreateOne = "lti.master.locations.create" + P_LocationsUpdateOne = "lti.master.locations.update" + P_LocationsDeleteOne = "lti.master.locations.delete" + + P_NonstocksGetAll = "lti.master.nonstocks.list" + P_NonstocksGetOne = "lti.master.nonstocks.detail" + P_NonstocksCreateOne = "lti.master.nonstocks.create" + P_NonstocksUpdateOne = "lti.master.nonstocks.update" + P_NonstocksDeleteOne = "lti.master.nonstocks.delete" + + P_ProductCategoriesGetAll = "lti.master.Product_categories.list" + P_ProductCategoriesGetOne = "lti.master.Product_categories.detail" + P_ProductCategoriesCreateOne = "lti.master.Product_categories.create" + P_ProductCategoriesUpdateOne = "lti.master.Product_categories.update" + P_ProductCategoriesDeleteOne = "lti.master.Product_categories.delete" + + P_ProductsGetAll = "lti.master.Products.list" + P_ProductsGetOne = "lti.master.Products.detail" + P_ProductsCreateOne = "lti.master.Products.create" + P_ProductsUpdateOne = "lti.master.Products.update" + P_ProductsDeleteOne = "lti.master.Products.delete" + + P_SuppliersGetAll = "lti.master.suppliers.list" + P_SuppliersGetOne = "lti.master.suppliers.detail" + P_SuppliersCreateOne = "lti.master.suppliers.create" + P_SuppliersUpdateOne = "lti.master.suppliers.update" + P_SuppliersDeleteOne = "lti.master.suppliers.delete" + + P_UomsGetAll = "lti.master.uoms.list" + P_UomsGetOne = "lti.master.uoms.detail" + P_UomsCreateOne = "lti.master.uoms.create" + P_UomsUpdateOne = "lti.master.uoms.update" + P_UomsDeleteOne = "lti.master.uoms.delete" + + P_WarehousesGetAll = "lti.master.warehouses.list" + P_WarehousesGetOne = "lti.master.warehouses.detail" + P_WarehousesCreateOne = "lti.master.warehouses.create" + P_WarehousesUpdateOne = "lti.master.warehouses.update" + P_WarehousesDeleteOne = "lti.master.warehouses.delete" + +) + + +const( + P_ChickinsCreateOne = "lti.production.chickins.create" + P_ChickinsGetOne = "lti.production.chickins.detail" + P_ChickinsApproval = "lti.production.chickins.approve" +) //recording const ( - PermissionRecordingRead = "recording.index" - PermissionRecordingCreate = "recording.create" - PermissionRecordingUpdate = "recording.update" - PermissionRecordingDelete = "recording.delete" + P_RecordingGetAll = "lti.production.recording.list" + P_RecordingGetOne = "lti.production.recording.detail" + P_RecordingCreateOne = "lti.production.recording.create" + P_RecordingUpdateOne = "lti.production.recording.update" + P_RecordingDeleteOne = "lti.production.recording.delete" + P_RecordingNextDay = "lti.production.recording.next_day" + P_RecordingApproval = "lti.production.recording.approve" +) + +const ( + P_PurchaseGetAll = "lti.Purchase.list" + P_PurchaseGetOne = "lti.Purchase.detail" + P_PurchaseCreateOne = "lti.Purchase.create" + P_PurchaseUpdateOne = "lti.Purchase.update" + P_PurchaseDeleteOne = "lti.Purchase.delete" + P_PurchaseItemDeleteOne = "lti.Purchase.delete.item" + P_PurchaseReceive = "lti.Purchase.receive" + P_PurchaseApprovalStaff = "lti.Purchase.approve.staff" + P_PurchaseApprovalManager = "lti.Purchase.approve.manager" +) + +const( + P_UserGetAll = "lti.users.list" + P_UserGetOne = "lti.users.detail" ) \ No newline at end of file diff --git a/internal/modules/approvals/route.go b/internal/modules/approvals/route.go index 5dd39616..cd479c03 100644 --- a/internal/modules/approvals/route.go +++ b/internal/modules/approvals/route.go @@ -15,5 +15,5 @@ func ApprovalRoutes(v1 fiber.Router, u user.UserService, s common.ApprovalServic route := v1.Group("/approvals") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) + route.Get("/", ctrl.GetAll,m.RequirePermissions(m.P_ApprovalGetAll)) } diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index ba18f3b9..059eb764 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -1,7 +1,7 @@ package closings import ( - // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + m "gitlab.com/mbugroup/lti-api.git/internal/middleware" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/controllers" closing "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/services" user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" @@ -13,14 +13,14 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService ctrl := controller.NewClosingController(s) route := v1.Group("/closing") - + route.Use(m.Auth(u)) // route.Get("/", m.Auth(u), ctrl.GetAll) // route.Post("/", m.Auth(u), ctrl.CreateOne) // route.Get("/:id", m.Auth(u), ctrl.GetOne) // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/", ctrl.GetAll) - route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan) - route.Get("/:projectFlockId", ctrl.GetClosingSummary) + route.Get("/",m.RequirePermissions(m.P_ClosingGetAll), ctrl.GetAll) + route.Get("/:project_flock_id/penjualan",m.RequirePermissions(m.P_ClosingPenjualan), ctrl.GetPenjualan) + route.Get("/:projectFlockId",m.RequirePermissions(m.P_ClosingGetSummary), ctrl.GetClosingSummary) } diff --git a/internal/modules/constants/route.go b/internal/modules/constants/route.go index 1da14371..46def610 100644 --- a/internal/modules/constants/route.go +++ b/internal/modules/constants/route.go @@ -12,6 +12,5 @@ func ConstantRoutes(v1 fiber.Router, s constant.ConstantService) { ctrl := controller.NewConstantController(s) route := v1.Group("/constants") - route.Get("/", ctrl.GetAll) } diff --git a/internal/modules/expenses/route.go b/internal/modules/expenses/route.go index 1fc5c07a..fa3191fa 100644 --- a/internal/modules/expenses/route.go +++ b/internal/modules/expenses/route.go @@ -22,16 +22,16 @@ func ExpenseRoutes(v1 fiber.Router, u user.UserService, s expense.ExpenseService // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) - route.Post("/approvals/manager", ctrl.Approval) - route.Post("/approvals/finance", ctrl.Approval) - route.Post("/:id/realizations", ctrl.CreateRealization) - route.Patch("/:id/realizations", ctrl.UpdateRealization) - route.Post("/:id/complete", ctrl.CompleteExpense) - route.Delete("/:id/documents/:documentId", ctrl.DeleteDocument) - route.Delete("/:id/realization-documents/:documentId", ctrl.DeleteRealizationDocument) + route.Get("/",m.RequirePermissions(m.P_ExpenseGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_ExpenseCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_ExpenseGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_ExpenseUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_ExpenseDeleteOne), ctrl.DeleteOne) + route.Post("/approvals/manager",m.RequirePermissions(m.P_ExpenseApprovalManager), ctrl.Approval) + route.Post("/approvals/finance",m.RequirePermissions(m.P_ExpenseApprovalFinance), ctrl.Approval) + route.Post("/:id/realizations",m.RequirePermissions(m.P_ExpenseCreateRealizations), ctrl.CreateRealization) + route.Patch("/:id/realizations",m.RequirePermissions(m.P_ExpenseUpdateRealizations), ctrl.UpdateRealization) + route.Post("/:id/complete",m.RequirePermissions(m.P_ExpenseCompleteExpense), ctrl.CompleteExpense) + route.Delete("/:id/documents/:documentId",m.RequirePermissions(m.P_ExpenseDocument), ctrl.DeleteDocument) + route.Delete("/:id/realization-documents/:documentId",m.RequirePermissions(m.P_ExpenseDocumentRealizations), ctrl.DeleteRealizationDocument) } diff --git a/internal/modules/inventory/adjustments/route.go b/internal/modules/inventory/adjustments/route.go index 8f58bb4d..f99fe01e 100644 --- a/internal/modules/inventory/adjustments/route.go +++ b/internal/modules/inventory/adjustments/route.go @@ -1,7 +1,7 @@ package adjustments import ( - // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + m "gitlab.com/mbugroup/lti-api.git/internal/middleware" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/controllers" adjustment "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/adjustments/services" user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" @@ -13,10 +13,10 @@ func AdjustmentRoutes(v1 fiber.Router, u user.UserService, s adjustment.Adjustme ctrl := controller.NewAdjustmentController(s) route := v1.Group("/adjustments") - + route.Use(m.Auth(u)) // Standard CRUD routes following master data pattern - route.Get("/", ctrl.AdjustmentHistory) // Get all with pagination and filters - route.Post("/", ctrl.Adjustment) // Create adjustment - route.Get("/:id", ctrl.GetOne) + route.Get("/",m.RequirePermissions(m.P_AdjustmentGetAll), ctrl.AdjustmentHistory) // Get all with pagination and filters + route.Post("/",m.RequirePermissions(m.P_AdjustmentCreate), ctrl.Adjustment) // Create adjustment + route.Get("/:id",m.RequirePermissions(m.P_AdjustmentGetOne), ctrl.GetOne) } diff --git a/internal/modules/inventory/product-stocks/route.go b/internal/modules/inventory/product-stocks/route.go index c7bb37f8..41714edc 100644 --- a/internal/modules/inventory/product-stocks/route.go +++ b/internal/modules/inventory/product-stocks/route.go @@ -1,7 +1,7 @@ package productStocks import ( - // m "gitlab.com/mbugroup/lti-api.git/internal/middleware" + m "gitlab.com/mbugroup/lti-api.git/internal/middleware" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-stocks/controllers" productStock "gitlab.com/mbugroup/lti-api.git/internal/modules/inventory/product-stocks/services" user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" @@ -13,13 +13,13 @@ func ProductStockRoutes(v1 fiber.Router, u user.UserService, s productStock.Prod ctrl := controller.NewProductStockController(s) route := v1.Group("/product-stocks") - +route.Use(m.Auth(u)) // route.Get("/", m.Auth(u), ctrl.GetAll) // route.Post("/", m.Auth(u), ctrl.CreateOne) // route.Get("/:id", m.Auth(u), ctrl.GetOne) // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/", ctrl.GetAll) - route.Get("/:id", ctrl.GetOne) + route.Get("/",m.RequirePermissions(m.P_ProductStockGetAll), ctrl.GetAll) + route.Get("/:id",m.RequirePermissions(m.P_ProductStockGetOne), ctrl.GetOne) } diff --git a/internal/modules/inventory/product-warehouses/route.go b/internal/modules/inventory/product-warehouses/route.go index 9c6c8e2b..81c06a08 100644 --- a/internal/modules/inventory/product-warehouses/route.go +++ b/internal/modules/inventory/product-warehouses/route.go @@ -15,7 +15,7 @@ func ProductWarehouseRoutes(v1 fiber.Router, u user.UserService, s productWareho route := v1.Group("/product-warehouses") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Get("/:id", ctrl.GetOne) + route.Get("/",m.RequirePermissions(m.P_ProductWarehousekGetAll), ctrl.GetAll) + route.Get("/:id",m.RequirePermissions(m.P_ProductWarehouseGetOne), ctrl.GetOne) } diff --git a/internal/modules/inventory/transfers/route.go b/internal/modules/inventory/transfers/route.go index f608af42..d24dbcb4 100644 --- a/internal/modules/inventory/transfers/route.go +++ b/internal/modules/inventory/transfers/route.go @@ -15,8 +15,8 @@ func TransferRoutes(v1 fiber.Router, u user.UserService, s transfer.TransferServ route := v1.Group("/transfers") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) + route.Get("/",m.RequirePermissions(m.P_TransferGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_TransferCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_TransferGetOne), ctrl.GetOne) } diff --git a/internal/modules/marketing/delivery-orderss/route.go b/internal/modules/marketing/delivery-orderss/route.go index c83330da..f4c08457 100644 --- a/internal/modules/marketing/delivery-orderss/route.go +++ b/internal/modules/marketing/delivery-orderss/route.go @@ -11,13 +11,12 @@ import ( func DeliveryOrdersRoutes(v1 fiber.Router, u user.UserService, s deliveryOrders.DeliveryOrdersService) { ctrl := controller.NewDeliveryOrdersController(s) - - v1.Get("/", ctrl.GetAll) - v1.Get("/:id", ctrl.GetOne) + v1.Use(m.Auth(u)) + v1.Get("/",m.RequirePermissions(m.P_DeliveryGetAll), ctrl.GetAll) + v1.Get("/:id",m.RequirePermissions(m.P_DeliveryGetOne), ctrl.GetOne) // Sisanya di group /delivery-orders route := v1.Group("/delivery-orders") - route.Use(m.Auth(u)) // route.Get("/", m.Auth(u), ctrl.GetAll) // route.Post("/", m.Auth(u), ctrl.CreateOne) @@ -25,7 +24,7 @@ func DeliveryOrdersRoutes(v1 fiber.Router, u user.UserService, s deliveryOrders. // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Post("/", ctrl.CreateOne) - route.Patch("/:id", ctrl.UpdateOne) + route.Post("/",m.RequirePermissions(m.P_DeliveryCreateOne), ctrl.CreateOne) + route.Patch("/:id",m.RequirePermissions(m.P_DeliveryUpdateOne), ctrl.UpdateOne) } diff --git a/internal/modules/marketing/sales-orders/route.go b/internal/modules/marketing/sales-orders/route.go index f87cea66..17249840 100644 --- a/internal/modules/marketing/sales-orders/route.go +++ b/internal/modules/marketing/sales-orders/route.go @@ -11,17 +11,16 @@ import ( func SalesOrdersRoutes(v1 fiber.Router, u user.UserService, s salesOrders.SalesOrdersService) { ctrl := controller.NewSalesOrdersController(s) - - v1.Delete("/:id", ctrl.DeleteOne) + v1.Use(m.Auth(u)) + v1.Delete("/:id",m.RequirePermissions(m.P_SalesOrderDelete), ctrl.DeleteOne) route := v1.Group("/sales-orders") - route.Use(m.Auth(u)) // route.Post("/", m.Auth(u), ctrl.CreateOne) // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Post("/", ctrl.CreateOne) - route.Patch("/:id", ctrl.UpdateOne) + route.Post("/",m.RequirePermissions(m.P_SalesOrderCreateOne), ctrl.CreateOne) + route.Patch("/:id",m.RequirePermissions(m.P_SalesOrderUpdateOne), ctrl.UpdateOne) - route.Post("/approvals", ctrl.Approval) + route.Post("/approvals",m.RequirePermissions(m.P_SalesOrderApproval), ctrl.Approval) } diff --git a/internal/modules/master/areas/route.go b/internal/modules/master/areas/route.go index 755a542e..0d715fb7 100644 --- a/internal/modules/master/areas/route.go +++ b/internal/modules/master/areas/route.go @@ -15,9 +15,9 @@ func AreaRoutes(v1 fiber.Router, u user.UserService, s area.AreaService) { route := v1.Group("/areas") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_AreaGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_AreaCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_AreaGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_AreaUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_AreaDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/banks/route.go b/internal/modules/master/banks/route.go index 2e5bed3b..678a834c 100644 --- a/internal/modules/master/banks/route.go +++ b/internal/modules/master/banks/route.go @@ -14,10 +14,9 @@ func BankRoutes(v1 fiber.Router, u user.UserService, s bank.BankService) { route := v1.Group("/banks") route.Use(m.Auth(u)) - - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_BanksGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_BanksCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_BanksGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_BanksUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_BanksDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/customers/route.go b/internal/modules/master/customers/route.go index d361e167..92f8139e 100644 --- a/internal/modules/master/customers/route.go +++ b/internal/modules/master/customers/route.go @@ -15,9 +15,9 @@ func CustomerRoutes(v1 fiber.Router, u user.UserService, s customer.CustomerServ route := v1.Group("/customers") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_CustomerGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_CustomerCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_CustomerGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_CustomerUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_CustomerDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/fcrs/route.go b/internal/modules/master/fcrs/route.go index 60633f16..06291ce4 100644 --- a/internal/modules/master/fcrs/route.go +++ b/internal/modules/master/fcrs/route.go @@ -15,9 +15,9 @@ func FcrRoutes(v1 fiber.Router, u user.UserService, s fcr.FcrService) { route := v1.Group("/fcrs") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_FcrGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_FcrCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_FcrGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_FcrUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_FcrDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/flocks/route.go b/internal/modules/master/flocks/route.go index 429d8dcd..046e014a 100644 --- a/internal/modules/master/flocks/route.go +++ b/internal/modules/master/flocks/route.go @@ -15,9 +15,9 @@ func FlockRoutes(v1 fiber.Router, u user.UserService, s flock.FlockService) { route := v1.Group("/flocks") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_FlocksGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_FlocksCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_FlocksGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_FlocksUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_FlocksDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/kandangs/route.go b/internal/modules/master/kandangs/route.go index 6a425b64..4cbf2793 100644 --- a/internal/modules/master/kandangs/route.go +++ b/internal/modules/master/kandangs/route.go @@ -15,9 +15,9 @@ func KandangRoutes(v1 fiber.Router, u user.UserService, s kandang.KandangService route := v1.Group("/kandangs") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_KandangsGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_KandangsCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_KandangsGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_KandangsUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_KandangsDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/locations/route.go b/internal/modules/master/locations/route.go index 68bce594..771e2d0d 100644 --- a/internal/modules/master/locations/route.go +++ b/internal/modules/master/locations/route.go @@ -15,9 +15,9 @@ func LocationRoutes(v1 fiber.Router, u user.UserService, s location.LocationServ route := v1.Group("/locations") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_LocationsGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_LocationsCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_LocationsGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_LocationsUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_LocationsDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/nonstocks/route.go b/internal/modules/master/nonstocks/route.go index 2aa7b838..6f2a2016 100644 --- a/internal/modules/master/nonstocks/route.go +++ b/internal/modules/master/nonstocks/route.go @@ -15,9 +15,9 @@ func NonstockRoutes(v1 fiber.Router, u user.UserService, s nonstock.NonstockServ route := v1.Group("/nonstocks") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_NonstocksGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_NonstocksCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_NonstocksGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_NonstocksUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_NonstocksDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/product-categories/route.go b/internal/modules/master/product-categories/route.go index 4a2262f9..1fa0532f 100644 --- a/internal/modules/master/product-categories/route.go +++ b/internal/modules/master/product-categories/route.go @@ -15,9 +15,9 @@ func ProductCategoryRoutes(v1 fiber.Router, u user.UserService, s productCategor route := v1.Group("/product-categories") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_ProductCategoriesGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_ProductCategoriesCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_ProductCategoriesGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_ProductCategoriesUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_ProductCategoriesDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/products/route.go b/internal/modules/master/products/route.go index 369d6ea8..04431bd4 100644 --- a/internal/modules/master/products/route.go +++ b/internal/modules/master/products/route.go @@ -15,9 +15,9 @@ func ProductRoutes(v1 fiber.Router, u user.UserService, s product.ProductService route := v1.Group("/products") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_ProductsGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_ProductsCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_ProductsGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_ProductsUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_ProductsDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/suppliers/route.go b/internal/modules/master/suppliers/route.go index 17271d4a..564ac725 100644 --- a/internal/modules/master/suppliers/route.go +++ b/internal/modules/master/suppliers/route.go @@ -15,9 +15,9 @@ func SupplierRoutes(v1 fiber.Router, u user.UserService, s supplier.SupplierServ route := v1.Group("/suppliers") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_SuppliersGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_SuppliersCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_SuppliersGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_SuppliersUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_SuppliersDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/uoms/route.go b/internal/modules/master/uoms/route.go index 53faa239..8ffbcb62 100644 --- a/internal/modules/master/uoms/route.go +++ b/internal/modules/master/uoms/route.go @@ -20,4 +20,10 @@ func UomRoutes(v1 fiber.Router, u user.UserService, s uom.UomService) { route.Get("/:id", ctrl.GetOne) route.Patch("/:id", ctrl.UpdateOne) route.Delete("/:id", ctrl.DeleteOne) + + route.Get("/",m.RequirePermissions(m.P_AreaGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_AreaCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_AreaGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_AreaUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_AreaDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/master/warehouses/route.go b/internal/modules/master/warehouses/route.go index 8acf4452..a08b04a5 100644 --- a/internal/modules/master/warehouses/route.go +++ b/internal/modules/master/warehouses/route.go @@ -15,9 +15,9 @@ func WarehouseRoutes(v1 fiber.Router, u user.UserService, s warehouse.WarehouseS route := v1.Group("/warehouses") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_WarehousesGetAll), ctrl.GetAll) + route.Post("/",m.RequirePermissions(m.P_WarehousesCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_WarehousesGetOne), ctrl.GetOne) + route.Patch("/:id",m.RequirePermissions(m.P_WarehousesUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_WarehousesDeleteOne), ctrl.DeleteOne) } diff --git a/internal/modules/production/chickins/route.go b/internal/modules/production/chickins/route.go index a558dd29..103a3655 100644 --- a/internal/modules/production/chickins/route.go +++ b/internal/modules/production/chickins/route.go @@ -16,9 +16,9 @@ func ChickinRoutes(v1 fiber.Router, u user.UserService, s chickin.ChickinService route.Use(m.Auth(u)) // route.Get("/", ctrl.GetAll) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) + route.Post("/",m.RequirePermissions(m.P_ChickinsCreateOne), ctrl.CreateOne) + route.Get("/:id",m.RequirePermissions(m.P_ChickinsGetOne), ctrl.GetOne) // route.Patch("/:id", ctrl.UpdateOne) // route.Delete("/:id", ctrl.DeleteOne) - route.Post("/approvals", ctrl.Approval) + route.Post("/approvals",m.RequirePermissions(m.P_ChickinsApproval), ctrl.Approval) } diff --git a/internal/modules/production/project-flock-kandangs/route.go b/internal/modules/production/project-flock-kandangs/route.go index d4dfec30..b382d1af 100644 --- a/internal/modules/production/project-flock-kandangs/route.go +++ b/internal/modules/production/project-flock-kandangs/route.go @@ -14,12 +14,6 @@ func ProjectFlockKandangRoutes(v1 fiber.Router, u user.UserService, s projectFlo route := v1.Group("/project-flock-kandangs") route.Use(m.Auth(u)) - // route.Get("/", m.Auth(u), ctrl.GetAll) - // route.Post("/", m.Auth(u), ctrl.CreateOne) - // route.Get("/:id", m.Auth(u), ctrl.GetOne) - // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) - // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/",m.RequirePermissions(m.P_ProjectFlockKandangsGetAll), ctrl.GetAll) route.Get("/:id",m.RequirePermissions(m.P_ProjectFlockKandangsGetOne), ctrl.GetOne) diff --git a/internal/modules/production/recordings/route.go b/internal/modules/production/recordings/route.go index 83b426db..f05d054d 100644 --- a/internal/modules/production/recordings/route.go +++ b/internal/modules/production/recordings/route.go @@ -15,11 +15,11 @@ func RecordingRoutes(v1 fiber.Router, u user.UserService, s recording.RecordingS route := v1.Group("/recordings") route.Use(m.Auth(u)) - route.Get("/", ctrl.GetAll) - route.Get("/next-day", ctrl.GetNextDay) - route.Post("/", ctrl.CreateOne) - route.Get("/:id", ctrl.GetOne) - route.Patch("/:id", ctrl.UpdateOne) - route.Post("/approvals", ctrl.Approve) - route.Delete("/:id", ctrl.DeleteOne) + route.Get("/",m.RequirePermissions(m.P_RecordingGetAll), ctrl.GetAll) + route.Get("/:id",m.RequirePermissions(m.P_RecordingGetOne), ctrl.GetOne) + route.Post("/",m.RequirePermissions(m.P_RecordingCreateOne), ctrl.CreateOne) + route.Patch("/:id",m.RequirePermissions(m.P_RecordingUpdateOne), ctrl.UpdateOne) + route.Delete("/:id",m.RequirePermissions(m.P_RecordingDeleteOne), ctrl.DeleteOne) + route.Get("/next-day",m.RequirePermissions(m.P_RecordingNextDay), ctrl.GetNextDay) + route.Post("/approvals",m.RequirePermissions(m.P_RecordingApproval), ctrl.Approve) } diff --git a/internal/modules/purchases/route.go b/internal/modules/purchases/route.go index 5145bc94..4be485e6 100644 --- a/internal/modules/purchases/route.go +++ b/internal/modules/purchases/route.go @@ -15,12 +15,12 @@ func Routes(router fiber.Router, purchaseService service.PurchaseService, userSe route := router.Group("/purchases") route.Use(m.Auth(userService)) - route.Get("/", ctrl.GetAll) - route.Get("/:id", ctrl.GetOne) - route.Post("/", ctrl.CreateOne) - route.Post("/:id/approvals/staff", ctrl.ApproveStaffPurchase) - route.Post("/:id/approvals/manager", ctrl.ApproveManagerPurchase) - route.Post("/:id/receipts", ctrl.ReceiveProducts) - route.Delete("/:id", ctrl.DeletePurchase) - route.Delete("/:id/items", ctrl.DeleteItems) + route.Get("/",m.RequirePermissions(m.P_PurchaseGetAll), ctrl.GetAll) + route.Get("/:id",m.RequirePermissions(m.P_PurchaseGetOne), ctrl.GetOne) + route.Post("/",m.RequirePermissions(m.P_PurchaseCreateOne), ctrl.CreateOne) + route.Post("/:id/approvals/staff",m.RequirePermissions(m.P_PurchaseApprovalStaff), ctrl.ApproveStaffPurchase) + route.Post("/:id/approvals/manager",m.RequirePermissions(m.P_PurchaseApprovalManager), ctrl.ApproveManagerPurchase) + route.Post("/:id/receipts",m.RequirePermissions(m.P_PurchaseReceive), ctrl.ReceiveProducts) + route.Delete("/:id",m.RequirePermissions(m.P_RecordingDeleteOne), ctrl.DeletePurchase) + route.Delete("/:id/items",m.RequirePermissions(m.P_PurchaseItemDeleteOne), ctrl.DeleteItems) } diff --git a/internal/modules/users/route.go b/internal/modules/users/route.go index 1093312f..d6aa03fe 100644 --- a/internal/modules/users/route.go +++ b/internal/modules/users/route.go @@ -14,9 +14,9 @@ func UserRoutes(v1 fiber.Router, s user.UserService) { route := v1.Group("/users") route.Use(m.Auth(s)) - route.Get("/", m.RequirePermissions("lti.users.list"), ctrl.GetAll) + route.Get("/", m.RequirePermissions(m.P_UserGetAll), ctrl.GetAll) // route.Post("/", ctrl.CreateOne) - route.Get("/:id", m.RequirePermissions("lti.users.detail"), ctrl.GetOne) + route.Get("/:id", m.RequirePermissions(m.P_UserGetOne), ctrl.GetOne) // route.Patch("/:id", ctrl.UpdateOne) // route.Delete("/:id", ctrl.DeleteOne) } From b8425c0f589de727483789977a83c512fee32653 Mon Sep 17 00:00:00 2001 From: kris Date: Thu, 11 Dec 2025 04:06:51 +0000 Subject: [PATCH 03/15] Edit .air.toml --- .air.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.air.toml b/.air.toml index 0c534172..c463b5b2 100644 --- a/.air.toml +++ b/.air.toml @@ -3,7 +3,7 @@ root = "." tmp_dir = "tmp" [build] -cmd = "go build -o ./tmp/main ./cmd/api" +cmd = "go build -buildvcs=false -o ./tmp/main ./cmd/api" bin = "tmp/main" full_bin = "APP_ENV=dev ./tmp/main" include_ext = ["go", "tpl", "tmpl", "html"] From fc49cef781f82d019821a181eaae9ebe60038f03 Mon Sep 17 00:00:00 2001 From: ragilap Date: Sun, 14 Dec 2025 23:15:30 +0700 Subject: [PATCH 04/15] add counting hpp-expedition by project --- .../controllers/closing.controller.go | 32 ++++++++++++ .../closings/dto/closingExpedition.dto.go | 18 +++++++ .../repositories/closing.repository.go | 49 +++++++++++++++++++ internal/modules/closings/route.go | 1 + .../closings/services/closing.service.go | 43 ++++++++++++++++ 5 files changed, 143 insertions(+) create mode 100644 internal/modules/closings/dto/closingExpedition.dto.go diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index dc39a666..1f61a775 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -188,3 +188,35 @@ func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error { Data: result, }) } + +func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { + param := c.Params("project_flock_id") + + projectFlockID, err := strconv.Atoi(param) + if err != nil || projectFlockID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid Project Flock Id") + } + + var projectFlockKandangID *uint + if raw := c.Query("project_flock_kandang_id"); raw != "" { + idInt, convErr := strconv.Atoi(raw) + if convErr != nil || idInt <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id") + } + idUint := uint(idInt) + projectFlockKandangID = &idUint + } + + result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), projectFlockKandangID) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get expedition HPP successfully", + Data: result, + }) +} diff --git a/internal/modules/closings/dto/closingExpedition.dto.go b/internal/modules/closings/dto/closingExpedition.dto.go new file mode 100644 index 00000000..f1b8628b --- /dev/null +++ b/internal/modules/closings/dto/closingExpedition.dto.go @@ -0,0 +1,18 @@ +package dto + +// ExpeditionCostItemDTO merepresentasikan biaya ekspedisi per vendor. +type ExpeditionCostItemDTO struct { + Id uint64 `json:"id"` + ExpeditionVendorID uint64 `json:"expedition_vendor_id"` + ExpeditionVendorName string `json:"expedition_vendor_name"` + Qty float64 `json:"qty"` + UnitPrice float64 `json:"unit_price"` + HPPAmount float64 `json:"hpp_amount"` +} + +// ExpeditionHPPDTO adalah struktur response utama untuk HPP Ekspedisi. +type ExpeditionHPPDTO struct { + ExpeditionCosts []ExpeditionCostItemDTO `json:"expedition_costs"` + TotalHPPAmount float64 `json:"total_hpp_amount"` +} + diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index fe555378..ecdfd125 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -9,12 +9,14 @@ import ( "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" ) type ClosingRepository interface { repository.BaseRepository[entity.ProjectFlock] GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) + GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) } type ClosingRepositoryImpl struct { @@ -44,6 +46,13 @@ type SapronakRow struct { Notes string `gorm:"column:notes"` } +type ExpeditionHPPRow struct { + SupplierID uint64 `gorm:"column:supplier_id"` + SupplierName string `gorm:"column:supplier_name"` + Qty float64 `gorm:"column:qty"` + TotalAmount float64 `gorm:"column:total_amount"` +} + type SapronakQueryParams struct { Type string WarehouseIDs []uint @@ -102,6 +111,46 @@ func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params Sapronak return rows, totalResults, nil } +func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlockID uint, projectFlockKandangID *uint) ([]ExpeditionHPPRow, error) { + db := r.DB().WithContext(ctx) + + if projectFlockID == 0 { + return nil, fmt.Errorf("invalid project flock id") + } + + query := db. + Table("expense_realizations AS er"). + Joins("JOIN expense_nonstocks ens ON ens.id = er.expense_nonstock_id"). + Joins("JOIN expenses e ON e.id = ens.expense_id"). + Joins("JOIN project_flock_kandangs pfk ON pfk.id = ens.project_flock_kandang_id"). + Joins("JOIN nonstocks n ON n.id = ens.nonstock_id"). + Joins("JOIN flags f ON f.flagable_id = n.id AND f.flagable_type = ?", entity.FlagableTypeNonstock). + Joins("JOIN suppliers s ON s.id = e.supplier_id"). + Where("pfk.project_flock_id = ?", projectFlockID). + Where("e.category = ?", "BOP"). + Where("UPPER(f.name) = ?", strings.ToUpper(string(utils.FlagEkspedisi))) + + if projectFlockKandangID != nil && *projectFlockKandangID != 0 { + query = query.Where("pfk.id = ?", *projectFlockKandangID) + } + + var rows []ExpeditionHPPRow + err := query. + Select( + "e.supplier_id AS supplier_id, " + + "s.name AS supplier_name, " + + "SUM(er.qty) AS qty, " + + "SUM(er.qty * er.price) AS total_amount", + ). + Group("e.supplier_id, s.name"). + Scan(&rows).Error + if err != nil { + return nil, err + } + + return rows, nil +} + const ( sapronakIncomingPurchasesSQL = ` SELECT diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 4d142f44..6a35ba06 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -25,4 +25,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/:project_flock_id/overhead", ctrl.GetOverhead) route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) + route.Get("/:project_flock_id/expedition-hpp", ctrl.GetExpeditionHPP) } diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index cfc22948..12357e46 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -31,6 +31,7 @@ type ClosingService interface { GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error) GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error) GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error) + GetExpeditionHPP(ctx *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error) } type closingService struct { @@ -379,3 +380,45 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.Ove return &result, nil } + +// GetExpeditionHPP menghitung HPP ekspedisi per vendor untuk sebuah project flock. +// Jika projectFlockKandangID tidak nil, maka hanya data untuk kandang tersebut yang dihitung. +func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, projectFlockKandangID *uint) (*dto.ExpeditionHPPDTO, error) { + if projectFlockID == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id") + } + + rows, err := s.Repository.GetExpeditionHPP(c.Context(), projectFlockID, projectFlockKandangID) + if err != nil { + s.Log.Errorf("Failed to get expedition HPP for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch expedition HPP") + } + + expeditionCosts := make([]dto.ExpeditionCostItemDTO, 0, len(rows)) + var totalHPP float64 + + for idx, row := range rows { + unitPrice := 0.0 + if row.Qty > 0 { + unitPrice = row.TotalAmount / row.Qty + } + + expeditionCosts = append(expeditionCosts, dto.ExpeditionCostItemDTO{ + Id: uint64(idx + 1), + ExpeditionVendorID: row.SupplierID, + ExpeditionVendorName: row.SupplierName, + Qty: row.Qty, + UnitPrice: unitPrice, + HPPAmount: row.TotalAmount, + }) + + totalHPP += row.TotalAmount + } + + result := &dto.ExpeditionHPPDTO{ + ExpeditionCosts: expeditionCosts, + TotalHPPAmount: totalHPP, + } + + return result, nil +} From cd739f41b94def0dbc651816711ea056e5d00590 Mon Sep 17 00:00:00 2001 From: ragilap Date: Tue, 16 Dec 2025 14:42:31 +0700 Subject: [PATCH 05/15] Feat(BE-339): make a report for purchasing supplier --- .../controllers/repport.controller.go | 50 +++++ .../repports/dto/repportPurchase.dto.go | 138 ++++++++++++ internal/modules/repports/module.go | 4 +- .../purchase_supplier.repository.go | 196 ++++++++++++++++++ internal/modules/repports/route.go | 1 + .../repports/services/repport.service.go | 69 +++++- .../validations/repport.validation.go | 13 ++ internal/response/response.go | 9 +- 8 files changed, 474 insertions(+), 6 deletions(-) create mode 100644 internal/modules/repports/dto/repportPurchase.dto.go create mode 100644 internal/modules/repports/repositories/purchase_supplier.repository.go diff --git a/internal/modules/repports/controllers/repport.controller.go b/internal/modules/repports/controllers/repport.controller.go index 21d3c49a..3e6c39d0 100644 --- a/internal/modules/repports/controllers/repport.controller.go +++ b/internal/modules/repports/controllers/repport.controller.go @@ -97,3 +97,53 @@ func (c *RepportController) GetMarketing(ctx *fiber.Ctx) error { Data: result, }) } + +func (c *RepportController) GetPurchaseSupplier(ctx *fiber.Ctx) error { + query := &validation.PurchaseSupplierQuery{ + Page: ctx.QueryInt("page", 1), + Limit: ctx.QueryInt("limit", 10), + AreaId: int64(ctx.QueryInt("area_id", 0)), + SupplierId: int64(ctx.QueryInt("supplier_id", 0)), + ProductId: int64(ctx.QueryInt("product_id", 0)), + ProductCategoryId: int64(ctx.QueryInt("product_category_id", 0)), + DateFrom: ctx.Query("date_from", ""), + DateTo: ctx.Query("date_to", ""), + SortBy: ctx.Query("sort_by", ""), + FilterBy: ctx.Query("filter_by", ""), + } + + if query.Page < 1 || query.Limit < 1 { + return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0") + } + + result, totalResults, err := c.RepportService.GetPurchaseSupplier(ctx, query) + if err != nil { + return err + } + + filters := map[string]interface{}{ + "area_id": query.AreaId, + "supplier_id": query.SupplierId, + "product_id": query.ProductId, + "product_category_id": query.ProductCategoryId, + "date_from": query.DateFrom, + "date_to": query.DateTo, + "sort_by": query.SortBy, + "filter_by": query.FilterBy, + } + + return ctx.Status(fiber.StatusOK). + JSON(response.SuccessWithPaginate[dto.PurchaseSupplierDTO]{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get supplier purchase recap successfully", + Meta: response.Meta{ + Page: query.Page, + Limit: query.Limit, + TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))), + TotalResults: totalResults, + Filters: filters, + }, + Data: result, + }) +} diff --git a/internal/modules/repports/dto/repportPurchase.dto.go b/internal/modules/repports/dto/repportPurchase.dto.go new file mode 100644 index 00000000..60fd0fee --- /dev/null +++ b/internal/modules/repports/dto/repportPurchase.dto.go @@ -0,0 +1,138 @@ +package dto + +import ( + "time" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + productDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/products/dto" + supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/dto" + warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto" +) + +type PurchaseSupplierRowDTO struct { + ReceiveDate string `json:"receive_date"` + PoDate string `json:"po_date"` + PoNumber string `json:"po_number"` + Product *productDTO.ProductRelationDTO `json:"product,omitempty"` + Warehouse *warehouseDTO.WarehouseRelationDTO `json:"warehouse,omitempty"` + Qty float64 `json:"qty"` + UnitPrice float64 `json:"unit_price"` + PurchaseValue float64 `json:"purchase_value"` + TransportUnitPrice float64 `json:"transport_unit_price"` + TransportValue float64 `json:"transport_value"` + TotalAmount float64 `json:"total_amount"` + Expedition string `json:"expedition"` + DeliveryNumber string `json:"delivery_number"` +} + +type PurchaseSupplierSummaryDTO struct { + TotalQty float64 `json:"total_qty"` + TotalPurchaseValue float64 `json:"total_purchase_value"` + TotalTransportValue float64 `json:"total_transport_value"` + TotalAmount float64 `json:"total_amount"` +} + +type PurchaseSupplierDTO struct { + Supplier *supplierDTO.SupplierRelationDTO `json:"supplier"` + Rows []PurchaseSupplierRowDTO `json:"rows"` + Summary PurchaseSupplierSummaryDTO `json:"summary"` +} + +func formatDatePtr(t *time.Time) string { + if t == nil || t.IsZero() { + return "" + } + return t.Format("02-Jan-2006") +} + +func ToPurchaseSupplierRowDTO(item *entity.PurchaseItem) PurchaseSupplierRowDTO { + row := PurchaseSupplierRowDTO{ + ReceiveDate: formatDatePtr(item.ReceivedDate), + Qty: item.TotalQty, + UnitPrice: item.Price, + } + + if item.Purchase != nil { + row.PoDate = formatDatePtr(item.Purchase.PoDate) + if item.Purchase.PoNumber != nil { + row.PoNumber = *item.Purchase.PoNumber + } + } + + if item.Product != nil && item.Product.Id != 0 { + product := productDTO.ToProductRelationDTO(*item.Product) + row.Product = &product + } + + if item.Warehouse != nil && item.Warehouse.Id != 0 { + warehouse := warehouseDTO.ToWarehouseRelationDTO(*item.Warehouse) + row.Warehouse = &warehouse + } + + qty := row.Qty + if qty < 0 { + qty = 0 + } + + row.PurchaseValue = row.UnitPrice * qty + + var transportUnit float64 + var expeditionName string + + if item.ExpenseNonstock != nil { + transportUnit = item.ExpenseNonstock.Price + + if item.ExpenseNonstock.Expense != nil && + item.ExpenseNonstock.Expense.Supplier != nil && + item.ExpenseNonstock.Expense.Supplier.Id != 0 { + expSupplier := item.ExpenseNonstock.Expense.Supplier + expeditionName = expSupplier.Name + } + } + + row.TransportUnitPrice = transportUnit + row.TransportValue = transportUnit * qty + row.TotalAmount = row.PurchaseValue + row.TransportValue + + if expeditionName == "" { + row.Expedition = "-" + } else { + row.Expedition = expeditionName + } + + if item.TravelNumber != nil && *item.TravelNumber != "" { + row.DeliveryNumber = *item.TravelNumber + } else { + row.DeliveryNumber = "-" + } + + return row +} + +func ToPurchaseSupplierDTO(supplier entity.Supplier, items []entity.PurchaseItem) PurchaseSupplierDTO { + var supplierDTORef *supplierDTO.SupplierRelationDTO + if supplier.Id != 0 { + mapped := supplierDTO.ToSupplierRelationDTO(supplier) + supplierDTORef = &mapped + } + + rows := make([]PurchaseSupplierRowDTO, 0, len(items)) + summary := PurchaseSupplierSummaryDTO{} + + for i := range items { + row := ToPurchaseSupplierRowDTO(&items[i]) + rows = append(rows, row) + + summary.TotalQty += row.Qty + summary.TotalPurchaseValue += row.PurchaseValue + summary.TotalTransportValue += row.TransportValue + summary.TotalAmount += row.TotalAmount + } + + return PurchaseSupplierDTO{ + Supplier: supplierDTORef, + Rows: rows, + Summary: summary, + } +} + diff --git a/internal/modules/repports/module.go b/internal/modules/repports/module.go index 4479b733..f3798f6a 100644 --- a/internal/modules/repports/module.go +++ b/internal/modules/repports/module.go @@ -7,6 +7,7 @@ import ( commonRepo "gitlab.com/mbugroup/lti-api.git/internal/common/repository" approvalService "gitlab.com/mbugroup/lti-api.git/internal/common/service" + repportRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/repositories" sRepport "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/services" expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" @@ -20,9 +21,10 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate * expenseRealizationRepository := expenseRepo.NewExpenseRealizationRepository(db) marketingDeliveryProductRepository := marketingRepo.NewMarketingDeliveryProductRepository(db) approvalRepository := commonRepo.NewApprovalRepository(db) + purchaseSupplierRepository := repportRepo.NewPurchaseSupplierRepository(db) approvalSvc := approvalService.NewApprovalService(approvalRepository) - repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, approvalSvc) + repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, approvalSvc, purchaseSupplierRepository) RepportRoutes(router, repportService) } diff --git a/internal/modules/repports/repositories/purchase_supplier.repository.go b/internal/modules/repports/repositories/purchase_supplier.repository.go new file mode 100644 index 00000000..cd282e8e --- /dev/null +++ b/internal/modules/repports/repositories/purchase_supplier.repository.go @@ -0,0 +1,196 @@ +package repositories + +import ( + "context" + "fmt" + "strings" + + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" + + "gorm.io/gorm" +) + +type PurchaseSupplierRepository interface { + GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error) + GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error) +} + +type purchaseSupplierRepositoryImpl struct { + db *gorm.DB +} + +func NewPurchaseSupplierRepository(db *gorm.DB) PurchaseSupplierRepository { + return &purchaseSupplierRepositoryImpl{db: db} +} + +func (r *purchaseSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filters *validation.PurchaseSupplierQuery) *gorm.DB { + // Tentukan kolom tanggal yang akan dipakai untuk filter + dateColumn := "purchase_items.received_date" + switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) { + case "po_date": + dateColumn = "purchases.po_date" + case "receive_date", "": + dateColumn = "purchase_items.received_date" + } + + db := r.db.WithContext(ctx). + Model(&entity.Supplier{}). + Joins("JOIN purchases ON purchases.supplier_id = suppliers.id"). + Joins("JOIN purchase_items ON purchase_items.purchase_id = purchases.id") + + if filters.SupplierId > 0 { + db = db.Where("suppliers.id = ?", filters.SupplierId) + } + + if filters.ProductId > 0 { + db = db.Where("purchase_items.product_id = ?", filters.ProductId) + } + + if filters.ProductCategoryId > 0 { + db = db. + Joins("JOIN products ON products.id = purchase_items.product_id"). + Where("products.product_category_id = ?", filters.ProductCategoryId) + } + + if filters.AreaId > 0 { + db = db. + Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id"). + Where("warehouses.area_id = ?", filters.AreaId) + } + + if filters.DateFrom != "" { + if dateFrom, err := utils.ParseDateString(filters.DateFrom); err == nil { + db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) + } + } + + if filters.DateTo != "" { + if dateTo, err := utils.ParseDateString(filters.DateTo); err == nil { + db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) + } + } + + return db +} + +func (r *purchaseSupplierRepositoryImpl) GetSuppliersWithPurchases(ctx context.Context, offset, limit int, filters *validation.PurchaseSupplierQuery) ([]entity.Supplier, int64, error) { + query := r.baseSupplierQuery(ctx, filters) + + var totalSuppliers int64 + if err := query. + Distinct("suppliers.id"). + Count(&totalSuppliers).Error; err != nil { + return nil, 0, err + } + + if totalSuppliers == 0 { + return []entity.Supplier{}, 0, nil + } + + if offset < 0 { + offset = 0 + } + + var supplierIDs []uint + if err := query. + Select("suppliers.id"). + Order("suppliers.id ASC"). + Offset(offset). + Limit(limit). + Pluck("suppliers.id", &supplierIDs).Error; err != nil { + return nil, 0, err + } + + if len(supplierIDs) == 0 { + return []entity.Supplier{}, totalSuppliers, nil + } + + var suppliers []entity.Supplier + if err := r.db.WithContext(ctx). + Where("id IN ?", supplierIDs). + Find(&suppliers).Error; err != nil { + return nil, 0, err + } + + return suppliers, totalSuppliers, nil +} + +func (r *purchaseSupplierRepositoryImpl) GetItemsBySuppliers(ctx context.Context, supplierIDs []uint, filters *validation.PurchaseSupplierQuery) ([]entity.PurchaseItem, error) { + if len(supplierIDs) == 0 { + return []entity.PurchaseItem{}, nil + } + + // Tentukan kolom tanggal yang akan dipakai untuk filter & sort + dateColumn := "purchase_items.received_date" + switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) { + case "po_date": + dateColumn = "purchases.po_date" + case "receive_date", "": + dateColumn = "purchase_items.received_date" + } + + orderDirection := "ASC" + switch strings.ToUpper(strings.TrimSpace(filters.SortBy)) { + case "DESC": + orderDirection = "DESC" + case "ASC", "": + orderDirection = "ASC" + } + + db := r.db.WithContext(ctx). + Model(&entity.PurchaseItem{}). + Preload("Purchase"). + Preload("Purchase.Supplier"). + Preload("Product"). + Preload("Product.ProductCategory"). + Preload("Warehouse"). + Preload("Warehouse.Area"). + Preload("Warehouse.Location"). + Preload("Warehouse.Kandang"). + Preload("ExpenseNonstock"). + Preload("ExpenseNonstock.Expense"). + Preload("ExpenseNonstock.Expense.Supplier"). + Joins("JOIN purchases ON purchases.id = purchase_items.purchase_id"). + Where("purchases.supplier_id IN ?", supplierIDs) + + if filters.ProductId > 0 { + db = db.Where("purchase_items.product_id = ?", filters.ProductId) + } + + if filters.ProductCategoryId > 0 { + db = db. + Joins("JOIN products ON products.id = purchase_items.product_id"). + Where("products.product_category_id = ?", filters.ProductCategoryId) + } + + if filters.AreaId > 0 { + db = db. + Joins("JOIN warehouses ON warehouses.id = purchase_items.warehouse_id"). + Where("warehouses.area_id = ?", filters.AreaId) + } + + if filters.DateFrom != "" { + if dateFrom, err := utils.ParseDateString(filters.DateFrom); err == nil { + db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) + } + } + + if filters.DateTo != "" { + if dateTo, err := utils.ParseDateString(filters.DateTo); err == nil { + db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) + } + } + + // Urutkan berdasarkan kolom tanggal yang dipilih dan arah sort + db = db.Order(fmt.Sprintf("%s %s", dateColumn, orderDirection)). + Order("purchase_items.id ASC") + + var items []entity.PurchaseItem + if err := db.Find(&items).Error; err != nil { + return nil, err + } + + return items, nil +} diff --git a/internal/modules/repports/route.go b/internal/modules/repports/route.go index 4aea831c..d24caac5 100644 --- a/internal/modules/repports/route.go +++ b/internal/modules/repports/route.go @@ -14,4 +14,5 @@ func RepportRoutes(v1 fiber.Router, s repport.RepportService) { route.Get("/expense", ctrl.GetExpense) route.Get("/marketing", ctrl.GetMarketing) + route.Get("/purchase-supplier", ctrl.GetPurchaseSupplier) } diff --git a/internal/modules/repports/services/repport.service.go b/internal/modules/repports/services/repport.service.go index 3adc5c0a..aa649871 100644 --- a/internal/modules/repports/services/repport.service.go +++ b/internal/modules/repports/services/repport.service.go @@ -2,6 +2,7 @@ package service import ( "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/dto" + repportRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/repositories" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/validations" "gitlab.com/mbugroup/lti-api.git/internal/utils" @@ -10,6 +11,8 @@ import ( expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories" + entity "gitlab.com/mbugroup/lti-api.git/internal/entities" + "github.com/go-playground/validator/v10" "github.com/gofiber/fiber/v2" "github.com/sirupsen/logrus" @@ -19,6 +22,7 @@ import ( type RepportService interface { GetExpense(ctx *fiber.Ctx, params *validation.ExpenseQuery) ([]dto.RepportExpenseListDTO, int64, error) GetMarketing(ctx *fiber.Ctx, params *validation.MarketingQuery) ([]dto.RepportMarketingListDTO, int64, error) + GetPurchaseSupplier(ctx *fiber.Ctx, params *validation.PurchaseSupplierQuery) ([]dto.PurchaseSupplierDTO, int64, error) } type repportService struct { @@ -27,15 +31,23 @@ type repportService struct { ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository ApprovalSvc approvalService.ApprovalService + PurchaseSupplierRepo repportRepo.PurchaseSupplierRepository } -func NewRepportService(validate *validator.Validate, expenseRealizationRepo expenseRepo.ExpenseRealizationRepository, marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository, approvalSvc approvalService.ApprovalService) RepportService { +func NewRepportService( + validate *validator.Validate, + expenseRealizationRepo expenseRepo.ExpenseRealizationRepository, + marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository, + approvalSvc approvalService.ApprovalService, + purchaseSupplierRepo repportRepo.PurchaseSupplierRepository, +) RepportService { return &repportService{ Log: utils.Log, Validate: validate, ExpenseRealizationRepo: expenseRealizationRepo, MarketingDeliveryRepo: marketingDeliveryRepo, ApprovalSvc: approvalSvc, + PurchaseSupplierRepo: purchaseSupplierRepo, } } @@ -113,3 +125,58 @@ func (s *repportService) GetMarketing(c *fiber.Ctx, params *validation.Marketing return dto.ToRepportMarketingListDTOs(deliveryProducts), total, nil } + +func (s *repportService) GetPurchaseSupplier(c *fiber.Ctx, params *validation.PurchaseSupplierQuery) ([]dto.PurchaseSupplierDTO, int64, error) { + if err := s.Validate.Struct(params); err != nil { + return nil, 0, err + } + + offset := (params.Page - 1) * params.Limit + if offset < 0 { + offset = 0 + } + + suppliers, totalSuppliers, err := s.PurchaseSupplierRepo.GetSuppliersWithPurchases(c.Context(), offset, params.Limit, params) + if err != nil { + return nil, 0, err + } + + if totalSuppliers == 0 || len(suppliers) == 0 { + return []dto.PurchaseSupplierDTO{}, totalSuppliers, nil + } + + supplierMap := make(map[uint]entity.Supplier, len(suppliers)) + supplierIDs := make([]uint, 0, len(suppliers)) + for _, supplier := range suppliers { + supplierMap[supplier.Id] = supplier + supplierIDs = append(supplierIDs, supplier.Id) + } + + items, err := s.PurchaseSupplierRepo.GetItemsBySuppliers(c.Context(), supplierIDs, params) + if err != nil { + return nil, 0, err + } + + itemsBySupplier := make(map[uint][]entity.PurchaseItem) + for _, item := range items { + if item.Purchase == nil { + continue + } + supplierID := item.Purchase.SupplierId + itemsBySupplier[supplierID] = append(itemsBySupplier[supplierID], item) + } + + result := make([]dto.PurchaseSupplierDTO, 0, len(supplierIDs)) + for _, supplierID := range supplierIDs { + supplier, exists := supplierMap[supplierID] + if !exists { + continue + } + + supplierItems := itemsBySupplier[supplierID] + dtoItem := dto.ToPurchaseSupplierDTO(supplier, supplierItems) + result = append(result, dtoItem) + } + + return result, totalSuppliers, nil +} diff --git a/internal/modules/repports/validations/repport.validation.go b/internal/modules/repports/validations/repport.validation.go index 7efc51f9..942eeaa8 100644 --- a/internal/modules/repports/validations/repport.validation.go +++ b/internal/modules/repports/validations/repport.validation.go @@ -27,3 +27,16 @@ type MarketingQuery struct { SalesPersonId int64 `query:"sales_person_id" validate:"omitempty"` MarketingId int64 `query:"marketing_id" validate:"omitempty"` } + +type PurchaseSupplierQuery struct { + Page int `query:"page" validate:"omitempty,min=1,gt=0"` + Limit int `query:"limit" validate:"omitempty,min=1,max=100,gt=0"` + AreaId int64 `query:"area_id" validate:"omitempty"` + SupplierId int64 `query:"supplier_id" validate:"omitempty"` + ProductId int64 `query:"product_id" validate:"omitempty"` + ProductCategoryId int64 `query:"product_category_id" validate:"omitempty"` + DateFrom string `query:"date_from" validate:"omitempty"` + DateTo string `query:"date_to" validate:"omitempty"` + SortBy string `query:"sort_by" validate:"omitempty"` + FilterBy string `query:"filter_by" validate:"omitempty"` +} diff --git a/internal/response/response.go b/internal/response/response.go index c4ecca0f..710d320e 100644 --- a/internal/response/response.go +++ b/internal/response/response.go @@ -14,10 +14,11 @@ type Success struct { } type Meta struct { - Page int `json:"page"` - Limit int `json:"limit"` - TotalPages int64 `json:"total_pages"` - TotalResults int64 `json:"total_results"` + Page int `json:"page"` + Limit int `json:"limit"` + TotalPages int64 `json:"total_pages"` + TotalResults int64 `json:"total_results"` + Filters interface{} `json:"filters,omitempty"` } type SuccessWithPaginate[T any] struct { From 3bfc401206eed1e05a6fff56057dbd7c40966856 Mon Sep 17 00:00:00 2001 From: ragilap Date: Wed, 17 Dec 2025 13:56:51 +0700 Subject: [PATCH 06/15] Feat(BE-334): make reporting closing hpp for project_flock_kandang --- .../controllers/closing.controller.go | 32 ++++++++++++++++++- internal/modules/closings/route.go | 1 + 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index 2b29429e..113aa667 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -251,7 +251,7 @@ func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { projectFlockID, err := strconv.Atoi(param) if err != nil || projectFlockID <= 0 { - return fiber.NewError(fiber.StatusBadRequest, "Invalid Project Flock Id") + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id") } var projectFlockKandangID *uint @@ -277,3 +277,33 @@ func (u *ClosingController) GetExpeditionHPP(c *fiber.Ctx) error { Data: result, }) } + +func (u *ClosingController) GetExpeditionHPPByKandang(c *fiber.Ctx) error { + projectParam := c.Params("project_flock_id") + kandangParam := c.Params("project_flock_kandang_id") + + projectFlockID, err := strconv.Atoi(projectParam) + if err != nil || projectFlockID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_id") + } + + pfkID, err := strconv.Atoi(kandangParam) + if err != nil || pfkID <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid project_flock_kandang_id") + } + + kandangID := uint(pfkID) + + result, err := u.ClosingService.GetExpeditionHPP(c, uint(projectFlockID), &kandangID) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Get expedition HPP successfully", + Data: result, + }) +} diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 526ab3b9..8a155bd0 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -28,4 +28,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) route.Get("/:project_flock_id/expedition-hpp", ctrl.GetExpeditionHPP) + route.Get("/:project_flock_id/:project_flock_kandang_id/expedition-hpp", ctrl.GetExpeditionHPPByKandang) } From e52a02b1c0e90855f5ec35d1c5c0d9de4f8a21fd Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 18 Dec 2025 11:30:55 +0700 Subject: [PATCH 07/15] Feat(BE-339): make reporting purchase per supplier with filterization --- .../repports/controllers/repport.controller.go | 8 ++++---- .../purchase_supplier.repository.go | 17 ++++++++--------- .../repports/validations/repport.validation.go | 4 ++-- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/internal/modules/repports/controllers/repport.controller.go b/internal/modules/repports/controllers/repport.controller.go index 3e6c39d0..039854c8 100644 --- a/internal/modules/repports/controllers/repport.controller.go +++ b/internal/modules/repports/controllers/repport.controller.go @@ -106,8 +106,8 @@ func (c *RepportController) GetPurchaseSupplier(ctx *fiber.Ctx) error { SupplierId: int64(ctx.QueryInt("supplier_id", 0)), ProductId: int64(ctx.QueryInt("product_id", 0)), ProductCategoryId: int64(ctx.QueryInt("product_category_id", 0)), - DateFrom: ctx.Query("date_from", ""), - DateTo: ctx.Query("date_to", ""), + StartDate: ctx.Query("start_date", ""), + EndDate: ctx.Query("end_date", ""), SortBy: ctx.Query("sort_by", ""), FilterBy: ctx.Query("filter_by", ""), } @@ -126,8 +126,8 @@ func (c *RepportController) GetPurchaseSupplier(ctx *fiber.Ctx) error { "supplier_id": query.SupplierId, "product_id": query.ProductId, "product_category_id": query.ProductCategoryId, - "date_from": query.DateFrom, - "date_to": query.DateTo, + "start_date": query.StartDate, + "end_date": query.EndDate, "sort_by": query.SortBy, "filter_by": query.FilterBy, } diff --git a/internal/modules/repports/repositories/purchase_supplier.repository.go b/internal/modules/repports/repositories/purchase_supplier.repository.go index cd282e8e..979623fc 100644 --- a/internal/modules/repports/repositories/purchase_supplier.repository.go +++ b/internal/modules/repports/repositories/purchase_supplier.repository.go @@ -26,7 +26,6 @@ func NewPurchaseSupplierRepository(db *gorm.DB) PurchaseSupplierRepository { } func (r *purchaseSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, filters *validation.PurchaseSupplierQuery) *gorm.DB { - // Tentukan kolom tanggal yang akan dipakai untuk filter dateColumn := "purchase_items.received_date" switch strings.ToLower(strings.TrimSpace(filters.FilterBy)) { case "po_date": @@ -60,14 +59,14 @@ func (r *purchaseSupplierRepositoryImpl) baseSupplierQuery(ctx context.Context, Where("warehouses.area_id = ?", filters.AreaId) } - if filters.DateFrom != "" { - if dateFrom, err := utils.ParseDateString(filters.DateFrom); err == nil { + if filters.StartDate != "" { + if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) } } - if filters.DateTo != "" { - if dateTo, err := utils.ParseDateString(filters.DateTo); err == nil { + if filters.EndDate != "" { + if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) } } @@ -171,14 +170,14 @@ func (r *purchaseSupplierRepositoryImpl) GetItemsBySuppliers(ctx context.Context Where("warehouses.area_id = ?", filters.AreaId) } - if filters.DateFrom != "" { - if dateFrom, err := utils.ParseDateString(filters.DateFrom); err == nil { + if filters.StartDate != "" { + if dateFrom, err := utils.ParseDateString(filters.StartDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) >= ?", dateColumn), dateFrom) } } - if filters.DateTo != "" { - if dateTo, err := utils.ParseDateString(filters.DateTo); err == nil { + if filters.EndDate != "" { + if dateTo, err := utils.ParseDateString(filters.EndDate); err == nil { db = db.Where(fmt.Sprintf("DATE(%s) <= ?", dateColumn), dateTo) } } diff --git a/internal/modules/repports/validations/repport.validation.go b/internal/modules/repports/validations/repport.validation.go index 942eeaa8..53ba22d7 100644 --- a/internal/modules/repports/validations/repport.validation.go +++ b/internal/modules/repports/validations/repport.validation.go @@ -35,8 +35,8 @@ type PurchaseSupplierQuery struct { SupplierId int64 `query:"supplier_id" validate:"omitempty"` ProductId int64 `query:"product_id" validate:"omitempty"` ProductCategoryId int64 `query:"product_category_id" validate:"omitempty"` - DateFrom string `query:"date_from" validate:"omitempty"` - DateTo string `query:"date_to" validate:"omitempty"` + StartDate string `query:"start_date" validate:"omitempty"` + EndDate string `query:"end_date" validate:"omitempty"` SortBy string `query:"sort_by" validate:"omitempty"` FilterBy string `query:"filter_by" validate:"omitempty"` } From d675b1e82651fba37d76ba3a33e0cd5bddacb6bc Mon Sep 17 00:00:00 2001 From: MacBook Air M1 Date: Thu, 18 Dec 2025 13:32:48 +0700 Subject: [PATCH 08/15] feat[BE-375]: get api closing data produksi --- .../controllers/closing.controller.go | 22 ++ internal/modules/closings/dto/closing.dto.go | 46 ++++ .../repositories/closing.repository.go | 164 +++++++++++++ internal/modules/closings/route.go | 1 + .../closings/services/closing.service.go | 218 ++++++++++++++++++ 5 files changed, 451 insertions(+) diff --git a/internal/modules/closings/controllers/closing.controller.go b/internal/modules/closings/controllers/closing.controller.go index dc39a666..cd105a48 100644 --- a/internal/modules/closings/controllers/closing.controller.go +++ b/internal/modules/closings/controllers/closing.controller.go @@ -188,3 +188,25 @@ func (u *ClosingController) GetClosingSapronak(c *fiber.Ctx) error { Data: result, }) } + +func (u *ClosingController) GetClosingDataProduksi(c *fiber.Ctx) error { + param := c.Params("projectFlockId") + + id, err := strconv.Atoi(param) + if err != nil || id <= 0 { + return fiber.NewError(fiber.StatusBadRequest, "Invalid projectFlockId") + } + + result, err := u.ClosingService.GetClosingDataProduksi(c, uint(id)) + if err != nil { + return err + } + + return c.Status(fiber.StatusOK). + JSON(response.Success{ + Code: fiber.StatusOK, + Status: "success", + Message: "Retrieved production data successfully", + Data: result, + }) +} diff --git a/internal/modules/closings/dto/closing.dto.go b/internal/modules/closings/dto/closing.dto.go index 1f1cb492..b3075776 100644 --- a/internal/modules/closings/dto/closing.dto.go +++ b/internal/modules/closings/dto/closing.dto.go @@ -58,6 +58,52 @@ type ClosingSummaryDTO struct { StatusClosing string `json:"closing_status"` } +type ClosingPurchaseDTO struct { + InitialPopulation int `json:"initial_population"` + ClaimCulling int `json:"claim_culling"` + FinalPopulation int `json:"final_population"` + FeedIn float64 `json:"feed_in"` + FeedUsed float64 `json:"feed_used"` + FeedUsedPerHead float64 `json:"feed_used_per_head"` +} + +type ClosingSalesDTO struct { + SalesPopulation int `json:"sales_population"` + SalesWeight float64 `json:"sales_weight"` + AverageWeight float64 `json:"average_weight"` + AverageSellingPrice float64 `json:"average_selling_price"` +} + +type ClosingEggSalesDTO struct { + EggPieces int `json:"egg_pieces"` + EggMassKg float64 `json:"egg_mass_kg"` + AverageEggWeightKg float64 `json:"average_egg_weight_kg"` + AverageSellingPrice float64 `json:"average_selling_price"` +} + +type ClosingPerformanceDTO struct { + Depletion float64 `json:"depletion"` + Age float64 `json:"age"` + MortalityStd float64 `json:"mortality_std"` + MortalityAct float64 `json:"mortality_act"` + DeffMortality float64 `json:"deff_mortality"` + FcrStd float64 `json:"fcr_std"` + FcrAct float64 `json:"fcr_act"` + DeffFcr float64 `json:"deff_fcr"` + Adg float64 `json:"adg"` +} + +type ClosingSalesGroupDTO struct { + ChickenProduction ClosingSalesDTO `json:"chicken_production"` + EggProduction ClosingEggSalesDTO `json:"egg_production"` +} + +type ClosingProductionReportDTO struct { + Purchase ClosingPurchaseDTO `json:"purchase"` + Sales ClosingSalesGroupDTO `json:"sales"` + Performance ClosingPerformanceDTO `json:"performance"` +} + func ToClosingSummaryDTO(project entity.ProjectFlock, statusProject, statusClosing string) ClosingSummaryDTO { history := project.KandangHistory diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index fe555378..186f48a2 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -9,12 +9,19 @@ import ( "gitlab.com/mbugroup/lti-api.git/internal/common/repository" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" validation "gitlab.com/mbugroup/lti-api.git/internal/modules/closings/validations" + "gitlab.com/mbugroup/lti-api.git/internal/utils" "gorm.io/gorm" ) type ClosingRepository interface { repository.BaseRepository[entity.ProjectFlock] GetSapronak(ctx context.Context, params SapronakQueryParams) ([]SapronakRow, int64, error) + SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error) + SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) + SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (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) + GetFcrStandardsByFcrID(ctx context.Context, fcrID uint) ([]entity.FcrStandard, error) } type ClosingRepositoryImpl struct { @@ -102,6 +109,163 @@ func (r *ClosingRepositoryImpl) GetSapronak(ctx context.Context, params Sapronak return rows, totalResults, nil } +func (r *ClosingRepositoryImpl) SumFeedPurchaseAndUsedByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, error) { + if len(projectFlockKandangIDs) == 0 { + return 0, 0, nil + } + + var purchaseAgg struct { + TotalIn float64 `gorm:"column:total_in"` + } + + err := r.DB().WithContext(ctx). + Table("purchase_items pi"). + Joins("JOIN flags f ON f.flagable_id = pi.product_id AND f.flagable_type = 'products'"). + Where("f.name = ?", "PAKAN"). + Where("pi.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Select("COALESCE(SUM(pi.total_qty), 0) AS total_in"). + Scan(&purchaseAgg).Error + if err != nil { + return 0, 0, err + } + + var usageAgg struct { + TotalUsed float64 `gorm:"column:total_used"` + } + + err = r.DB().WithContext(ctx). + Table("recording_stocks rs"). + Joins("JOIN product_warehouses pw ON pw.id = rs.product_warehouse_id"). + Joins("JOIN products prod ON prod.id = pw.product_id"). + Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products"). + Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Where("f.name = ?", "PAKAN"). + Select("COALESCE(SUM(COALESCE(rs.usage_qty, 0) + COALESCE(rs.pending_qty, 0)), 0) AS total_used"). + Scan(&usageAgg).Error + if err != nil { + return 0, 0, err + } + + return purchaseAgg.TotalIn, usageAgg.TotalUsed, nil +} + +func (r *ClosingRepositoryImpl) SumClaimCullingByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, error) { + if len(projectFlockKandangIDs) == 0 { + return 0, nil + } + + var agg struct { + Total float64 `gorm:"column:total_culling"` + } + + err := r.DB().WithContext(ctx). + Table("recording_depletions rd"). + Joins("JOIN product_warehouses pw ON pw.id = rd.product_warehouse_id"). + Joins("JOIN products prod ON prod.id = pw.product_id"). + Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products"). + Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Where("f.name = ?", utils.FlagAyamCulling). + Select("COALESCE(SUM(rd.qty), 0) AS total_culling"). + Scan(&agg).Error + if err != nil { + return 0, err + } + + return agg.Total, nil +} + +func (r *ClosingRepositoryImpl) SumMarketingWeightAndQtyByProjectFlockKandangIDs(ctx context.Context, projectFlockKandangIDs []uint) (float64, float64, float64, error) { + if len(projectFlockKandangIDs) == 0 { + return 0, 0, 0, nil + } + + var agg struct { + TotalWeight float64 `gorm:"column:total_weight"` + TotalQty float64 `gorm:"column:total_qty"` + TotalPrice float64 `gorm:"column:total_price"` + } + + err := r.DB().WithContext(ctx). + Table("marketing_products mp"). + Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id"). + Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Select("COALESCE(SUM(mp.total_weight), 0) AS total_weight, COALESCE(SUM(mp.qty), 0) AS total_qty, COALESCE(SUM(mp.total_price), 0) AS total_price"). + Scan(&agg).Error + if err != nil { + return 0, 0, 0, err + } + + return agg.TotalWeight, agg.TotalQty, agg.TotalPrice, nil +} + +func (r *ClosingRepositoryImpl) SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, float64, float64, error) { + if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 { + return 0, 0, 0, nil + } + + var agg struct { + TotalWeight float64 `gorm:"column:total_weight"` + TotalQty float64 `gorm:"column:total_qty"` + TotalPrice float64 `gorm:"column:total_price"` + } + + err := r.DB().WithContext(ctx). + Table("marketing_products mp"). + Joins("JOIN product_warehouses pw ON pw.id = mp.product_warehouse_id"). + Joins("JOIN products prod ON prod.id = pw.product_id"). + Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products"). + Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Where("f.name IN ?", flagNames). + Select("COALESCE(SUM(mp.total_weight), 0) AS total_weight, COALESCE(SUM(mp.qty), 0) AS total_qty, COALESCE(SUM(mp.total_price), 0) AS total_price"). + Scan(&agg).Error + if err != nil { + return 0, 0, 0, err + } + + return agg.TotalWeight, agg.TotalQty, agg.TotalPrice, nil +} + +func (r *ClosingRepositoryImpl) SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(ctx context.Context, projectFlockKandangIDs []uint, flagNames []string) (float64, error) { + if len(projectFlockKandangIDs) == 0 || len(flagNames) == 0 { + return 0, nil + } + + var agg struct { + TotalQty float64 `gorm:"column:total_qty"` + } + + err := r.DB().WithContext(ctx). + Table("recording_eggs re"). + Joins("JOIN product_warehouses pw ON pw.id = re.product_warehouse_id"). + Joins("JOIN products prod ON prod.id = pw.product_id"). + Joins("JOIN flags f ON f.flagable_id = prod.id AND f.flagable_type = ?", "products"). + Where("pw.project_flock_kandang_id IN ?", projectFlockKandangIDs). + Where("f.name IN ?", flagNames). + Select("COALESCE(SUM(re.qty), 0) AS total_qty"). + Scan(&agg).Error + if err != nil { + return 0, err + } + + 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 +} + const ( sapronakIncomingPurchasesSQL = ` SELECT diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 4d142f44..6de2dc0b 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -25,4 +25,5 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/:project_flock_id/overhead", ctrl.GetOverhead) route.Get("/:projectFlockId", ctrl.GetClosingSummary) route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) + route.Get("/:projectFlockId/data-produksi", ctrl.GetClosingDataProduksi) } diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index cfc22948..f8957a99 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -3,6 +3,7 @@ package service import ( "context" "errors" + "math" "strconv" commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" @@ -30,6 +31,7 @@ type ClosingService interface { GetPenjualan(ctx *fiber.Ctx, projectFlockID uint) ([]entity.MarketingDeliveryProduct, error) GetClosingSummary(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingSummaryDTO, error) GetOverhead(ctx *fiber.Ctx, projectFlockID uint) (*dto.OverheadListDTO, error) + GetClosingDataProduksi(ctx *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error) GetClosingSapronak(ctx *fiber.Ctx, projectFlockID uint, params *validation.SapronakQuery) ([]dto.ClosingSapronakItemDTO, int64, error) } @@ -379,3 +381,219 @@ func (s closingService) GetOverhead(c *fiber.Ctx, projectFlockID uint) (*dto.Ove return &result, nil } + +func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint) (*dto.ClosingProductionReportDTO, error) { + if projectFlockID == 0 { + return nil, fiber.NewError(fiber.StatusBadRequest, "Invalid project flock id") + } + + project, err := s.Repository.GetByID(c.Context(), projectFlockID, s.withClosingRelations) + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, fiber.NewError(fiber.StatusNotFound, "Project flock not found") + } + if err != nil { + s.Log.Errorf("Failed get project flock %d for closing data produksi: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock") + } + + var population float64 + for _, history := range project.KandangHistory { + for _, chickin := range history.Chickins { + population += chickin.UsageQty + chickin.PendingUsageQty + } + } + + projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID) + if err != nil { + s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch project flock kandangs") + } + + feedIn, feedUsed, err := s.Repository.SumFeedPurchaseAndUsedByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs) + if err != nil { + s.Log.Errorf("Failed to sum feed purchase/used qty for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch feed purchase data") + } + + claimCulling, err := s.Repository.SumClaimCullingByProjectFlockKandangIDs(c.Context(), projectFlockKandangIDs) + if err != nil { + s.Log.Errorf("Failed to sum claim culling for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch claim culling data") + } + + 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") + } + } + // masih dummy, karena tab penjualan agenya masih dummy juga + age := 1.0 + + feedUsedPerHead := 0.0 + if population > 0 { + feedUsedPerHead = feedUsed / population + } + + purchase := dto.ClosingPurchaseDTO{ + InitialPopulation: int(population), + ClaimCulling: int(claimCulling), + FinalPopulation: int(finalPopulation), + FeedIn: feedIn, + FeedUsed: feedUsed, + FeedUsedPerHead: feedUsedPerHead, + } + + chickenFlagNames := []string{string(utils.FlagPullet)} + chickenSalesWeight, chickenSalesQty, chickenSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, chickenFlagNames) + if err != nil { + s.Log.Errorf("Failed to fetch chicken sales data for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch chicken sales data") + } + + var chickenAverageWeight float64 + if chickenSalesQty > 0 { + chickenAverageWeight = chickenSalesWeight / chickenSalesQty + } + + var chickenAverageSellingPrice float64 + if chickenSalesWeight > 0 { + chickenAverageSellingPrice = chickenSalesPrice / chickenSalesWeight + } + + eggFlagNames := []string{ + string(utils.FlagTelur), + string(utils.FlagTelurUtuh), + string(utils.FlagTelurPecah), + string(utils.FlagTelurPutih), + string(utils.FlagTelurRetak), + } + eggSalesWeight, eggSalesQty, eggSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) + if err != nil { + s.Log.Errorf("Failed to fetch egg sales data for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg sales data") + } + + var averageEggWeight float64 + if eggSalesQty > 0 { + averageEggWeight = eggSalesWeight / eggSalesQty + } + + var averageEggSellingPrice float64 + if eggSalesWeight > 0 { + averageEggSellingPrice = eggSalesPrice / eggSalesWeight + } + + chickenSales := dto.ClosingSalesDTO{ + SalesPopulation: int(chickenSalesQty), + SalesWeight: chickenSalesWeight, + AverageWeight: chickenAverageWeight, + AverageSellingPrice: chickenAverageSellingPrice, + } + + eggSales := dto.ClosingEggSalesDTO{ + EggPieces: int(eggSalesQty), + EggMassKg: eggSalesWeight, + AverageEggWeightKg: averageEggWeight, + AverageSellingPrice: averageEggSellingPrice, + } + + harvestEggQty, err := s.Repository.SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) + if err != nil { + s.Log.Errorf("Failed to fetch recording egg qty for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg harvest data") + } + + chickenDepletion := population - chickenSalesQty + if chickenDepletion < 0 { + chickenDepletion = 0 + } + eggDepletion := harvestEggQty - eggSalesQty + if eggDepletion < 0 { + eggDepletion = 0 + } + + chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards) + eggPerformance := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards) + + sales := dto.ClosingSalesGroupDTO{ + ChickenProduction: chickenSales, + EggProduction: eggSales, + } + + performance := dto.ClosingPerformanceDTO{ + Depletion: chickenPerformance.Depletion, + Age: age, + MortalityStd: chickenPerformance.MortalityStd, + MortalityAct: chickenPerformance.MortalityAct, + DeffMortality: chickenPerformance.DeffMortality, + FcrStd: eggPerformance.FcrStd, + FcrAct: eggPerformance.FcrAct, + DeffFcr: eggPerformance.DeffFcr, + Adg: eggPerformance.Adg, + } + + result := dto.ClosingProductionReportDTO{ + Purchase: purchase, + Sales: sales, + Performance: performance, + } + + return &result, nil +} + +func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopulation, depletion, age float64, standards []entity.FcrStandard) dto.ClosingPerformanceDTO { + mortalityStd, fcrStd := closestFcrValues(standards, averageWeight) + + fcrAct := 0.0 + if totalWeight > 0 { + fcrAct = feedUsed / totalWeight + } + + mortalityAct := 0.0 + if basePopulation > 0 { + mortalityAct = (depletion / basePopulation) * 100 + } + + deffMortality := mortalityStd - mortalityAct + deffFcr := fcrStd - fcrAct + + adg := 0.0 + if age > 0 { + adg = averageWeight / age + } + + return dto.ClosingPerformanceDTO{ + Depletion: depletion, + Age: age, + MortalityStd: mortalityStd, + MortalityAct: mortalityAct, + DeffMortality: deffMortality, + FcrStd: fcrStd, + FcrAct: fcrAct, + DeffFcr: deffFcr, + Adg: adg, + } +} + +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 +} From 047162699e385a295be9aca1417b18557956d2a9 Mon Sep 17 00:00:00 2001 From: MacBook Air M1 Date: Thu, 18 Dec 2025 15:25:15 +0700 Subject: [PATCH 09/15] adjust response api closing data produksi --- internal/modules/closings/dto/closing.dto.go | 12 +- .../closings/services/closing.service.go | 120 ++++++++++-------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/internal/modules/closings/dto/closing.dto.go b/internal/modules/closings/dto/closing.dto.go index b3075776..429495b7 100644 --- a/internal/modules/closings/dto/closing.dto.go +++ b/internal/modules/closings/dto/closing.dto.go @@ -71,31 +71,31 @@ type ClosingSalesDTO struct { SalesPopulation int `json:"sales_population"` SalesWeight float64 `json:"sales_weight"` AverageWeight float64 `json:"average_weight"` - AverageSellingPrice float64 `json:"average_selling_price"` + AverageSellingPrice float64 `json:"chicken_average_selling_price"` } type ClosingEggSalesDTO struct { EggPieces int `json:"egg_pieces"` EggMassKg float64 `json:"egg_mass_kg"` AverageEggWeightKg float64 `json:"average_egg_weight_kg"` - AverageSellingPrice float64 `json:"average_selling_price"` + AverageSellingPrice float64 `json:"egg_average_selling_price"` } type ClosingPerformanceDTO struct { Depletion float64 `json:"depletion"` - Age float64 `json:"age"` + Age float64 `json:"age_day"` MortalityStd float64 `json:"mortality_std"` MortalityAct float64 `json:"mortality_act"` DeffMortality float64 `json:"deff_mortality"` FcrStd float64 `json:"fcr_std"` FcrAct float64 `json:"fcr_act"` DeffFcr float64 `json:"deff_fcr"` - Adg float64 `json:"adg"` + Awg float64 `json:"awg"` } type ClosingSalesGroupDTO struct { - ChickenProduction ClosingSalesDTO `json:"chicken_production"` - EggProduction ClosingEggSalesDTO `json:"egg_production"` + Chicken ClosingSalesDTO `json:"chicken"` + Egg *ClosingEggSalesDTO `json:"egg,omitempty"` } type ClosingProductionReportDTO struct { diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index f8957a99..e5479f35 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -5,6 +5,7 @@ import ( "errors" "math" "strconv" + "strings" commonSvc "gitlab.com/mbugroup/lti-api.git/internal/common/service" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" @@ -403,6 +404,8 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint } } + isGrowing := strings.EqualFold(project.Category, string(utils.ProjectFlockCategoryGrowing)) + projectFlockKandangIDs, err := s.getProjectFlockKandangIDs(c.Context(), projectFlockID) if err != nil { s.Log.Errorf("Failed to fetch project flock kandangs for %d: %+v", projectFlockID, err) @@ -431,7 +434,7 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch FCR standard data") } } - // masih dummy, karena tab penjualan agenya masih dummy juga + // masih dummy, karena tab penjualan agenya masih dummy age := 1.0 feedUsedPerHead := 0.0 @@ -465,29 +468,6 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint chickenAverageSellingPrice = chickenSalesPrice / chickenSalesWeight } - eggFlagNames := []string{ - string(utils.FlagTelur), - string(utils.FlagTelurUtuh), - string(utils.FlagTelurPecah), - string(utils.FlagTelurPutih), - string(utils.FlagTelurRetak), - } - eggSalesWeight, eggSalesQty, eggSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) - if err != nil { - s.Log.Errorf("Failed to fetch egg sales data for project flock %d: %+v", projectFlockID, err) - return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg sales data") - } - - var averageEggWeight float64 - if eggSalesQty > 0 { - averageEggWeight = eggSalesWeight / eggSalesQty - } - - var averageEggSellingPrice float64 - if eggSalesWeight > 0 { - averageEggSellingPrice = eggSalesPrice / eggSalesWeight - } - chickenSales := dto.ClosingSalesDTO{ SalesPopulation: int(chickenSalesQty), SalesWeight: chickenSalesWeight, @@ -495,34 +475,65 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint AverageSellingPrice: chickenAverageSellingPrice, } - eggSales := dto.ClosingEggSalesDTO{ - EggPieces: int(eggSalesQty), - EggMassKg: eggSalesWeight, - AverageEggWeightKg: averageEggWeight, - AverageSellingPrice: averageEggSellingPrice, - } - - harvestEggQty, err := s.Repository.SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) - if err != nil { - s.Log.Errorf("Failed to fetch recording egg qty for project flock %d: %+v", projectFlockID, err) - return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg harvest data") - } - chickenDepletion := population - chickenSalesQty if chickenDepletion < 0 { chickenDepletion = 0 } - eggDepletion := harvestEggQty - eggSalesQty - if eggDepletion < 0 { - eggDepletion = 0 - } chickenPerformance := calculatePerformanceMetrics(chickenAverageWeight, chickenSalesWeight, feedUsed, population, chickenDepletion, age, standards) - eggPerformance := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards) + + var eggSales *dto.ClosingEggSalesDTO + var eggPerformance *dto.ClosingPerformanceDTO + if !isGrowing { + eggFlagNames := []string{ + string(utils.FlagTelur), + string(utils.FlagTelurUtuh), + string(utils.FlagTelurPecah), + string(utils.FlagTelurPutih), + string(utils.FlagTelurRetak), + } + + eggSalesWeight, eggSalesQty, eggSalesPrice, err := s.Repository.SumMarketingWeightAndQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) + if err != nil { + s.Log.Errorf("Failed to fetch egg sales data for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg sales data") + } + + var averageEggWeight float64 + if eggSalesQty > 0 { + averageEggWeight = eggSalesWeight / eggSalesQty + } + + var averageEggSellingPrice float64 + if eggSalesWeight > 0 { + averageEggSellingPrice = eggSalesPrice / eggSalesWeight + } + + eggSales = &dto.ClosingEggSalesDTO{ + EggPieces: int(eggSalesQty), + EggMassKg: eggSalesWeight, + AverageEggWeightKg: averageEggWeight, + AverageSellingPrice: averageEggSellingPrice, + } + + harvestEggQty, err := s.Repository.SumRecordingEggQtyByProjectFlockKandangIDsAndFlagNames(c.Context(), projectFlockKandangIDs, eggFlagNames) + if err != nil { + s.Log.Errorf("Failed to fetch recording egg qty for project flock %d: %+v", projectFlockID, err) + return nil, fiber.NewError(fiber.StatusInternalServerError, "Failed to fetch egg harvest data") + } + + eggDepletion := harvestEggQty - eggSalesQty + if eggDepletion < 0 { + eggDepletion = 0 + } + + eggPerf := calculatePerformanceMetrics(averageEggWeight, eggSalesWeight, feedUsed, harvestEggQty, eggDepletion, age, standards) + eggPerformance = &eggPerf + } sales := dto.ClosingSalesGroupDTO{ - ChickenProduction: chickenSales, - EggProduction: eggSales, + Chicken: chickenSales, + Egg: eggSales, } performance := dto.ClosingPerformanceDTO{ @@ -531,10 +542,17 @@ func (s closingService) GetClosingDataProduksi(c *fiber.Ctx, projectFlockID uint MortalityStd: chickenPerformance.MortalityStd, MortalityAct: chickenPerformance.MortalityAct, DeffMortality: chickenPerformance.DeffMortality, - FcrStd: eggPerformance.FcrStd, - FcrAct: eggPerformance.FcrAct, - DeffFcr: eggPerformance.DeffFcr, - Adg: eggPerformance.Adg, + } + if eggPerformance != nil { + performance.FcrStd = eggPerformance.FcrStd + performance.FcrAct = eggPerformance.FcrAct + performance.DeffFcr = eggPerformance.DeffFcr + performance.Awg = eggPerformance.Awg + } else { + performance.FcrStd = chickenPerformance.FcrStd + performance.FcrAct = chickenPerformance.FcrAct + performance.DeffFcr = chickenPerformance.DeffFcr + performance.Awg = chickenPerformance.Awg } result := dto.ClosingProductionReportDTO{ @@ -562,9 +580,9 @@ func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopul deffMortality := mortalityStd - mortalityAct deffFcr := fcrStd - fcrAct - adg := 0.0 + awg := 0.0 if age > 0 { - adg = averageWeight / age + awg = averageWeight / age } return dto.ClosingPerformanceDTO{ @@ -576,7 +594,7 @@ func calculatePerformanceMetrics(averageWeight, totalWeight, feedUsed, basePopul FcrStd: fcrStd, FcrAct: fcrAct, DeffFcr: deffFcr, - Adg: adg, + Awg: awg, } } From 14a4d9e944374478eb6e74708734069b0a029763 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 18 Dec 2025 16:02:57 +0700 Subject: [PATCH 10/15] Feat(BE-334):Fixing dto closing hpp expedisi --- internal/modules/closings/dto/closingExpedition.dto.go | 4 ---- .../dto/{sapronak.dto.go => closingSapronak.dto.go} | 0 .../modules/closings/repositories/closing.repository.go | 5 +---- internal/modules/closings/services/closing.service.go | 8 -------- 4 files changed, 1 insertion(+), 16 deletions(-) rename internal/modules/closings/dto/{sapronak.dto.go => closingSapronak.dto.go} (100%) diff --git a/internal/modules/closings/dto/closingExpedition.dto.go b/internal/modules/closings/dto/closingExpedition.dto.go index f1b8628b..5f8a09d4 100644 --- a/internal/modules/closings/dto/closingExpedition.dto.go +++ b/internal/modules/closings/dto/closingExpedition.dto.go @@ -3,10 +3,7 @@ package dto // ExpeditionCostItemDTO merepresentasikan biaya ekspedisi per vendor. type ExpeditionCostItemDTO struct { Id uint64 `json:"id"` - ExpeditionVendorID uint64 `json:"expedition_vendor_id"` ExpeditionVendorName string `json:"expedition_vendor_name"` - Qty float64 `json:"qty"` - UnitPrice float64 `json:"unit_price"` HPPAmount float64 `json:"hpp_amount"` } @@ -15,4 +12,3 @@ type ExpeditionHPPDTO struct { ExpeditionCosts []ExpeditionCostItemDTO `json:"expedition_costs"` TotalHPPAmount float64 `json:"total_hpp_amount"` } - diff --git a/internal/modules/closings/dto/sapronak.dto.go b/internal/modules/closings/dto/closingSapronak.dto.go similarity index 100% rename from internal/modules/closings/dto/sapronak.dto.go rename to internal/modules/closings/dto/closingSapronak.dto.go diff --git a/internal/modules/closings/repositories/closing.repository.go b/internal/modules/closings/repositories/closing.repository.go index 14854430..0214d739 100644 --- a/internal/modules/closings/repositories/closing.repository.go +++ b/internal/modules/closings/repositories/closing.repository.go @@ -55,9 +55,7 @@ type SapronakRow struct { } type ExpeditionHPPRow struct { - SupplierID uint64 `gorm:"column:supplier_id"` SupplierName string `gorm:"column:supplier_name"` - Qty float64 `gorm:"column:qty"` TotalAmount float64 `gorm:"column:total_amount"` } @@ -147,7 +145,6 @@ func (r *ClosingRepositoryImpl) GetExpeditionHPP(ctx context.Context, projectFlo Select( "e.supplier_id AS supplier_id, " + "s.name AS supplier_name, " + - "SUM(er.qty) AS qty, " + "SUM(er.qty * er.price) AS total_amount", ). Group("e.supplier_id, s.name"). @@ -645,4 +642,4 @@ func (r *ClosingRepositoryImpl) FetchSapronakTransfers(ctx context.Context, kand return fmt.Sprintf("TRF-%d", row.ID) }) return in, out, nil -} \ No newline at end of file +} diff --git a/internal/modules/closings/services/closing.service.go b/internal/modules/closings/services/closing.service.go index d6e24e7f..afba0a9d 100644 --- a/internal/modules/closings/services/closing.service.go +++ b/internal/modules/closings/services/closing.service.go @@ -398,17 +398,9 @@ func (s closingService) GetExpeditionHPP(c *fiber.Ctx, projectFlockID uint, proj var totalHPP float64 for idx, row := range rows { - unitPrice := 0.0 - if row.Qty > 0 { - unitPrice = row.TotalAmount / row.Qty - } - expeditionCosts = append(expeditionCosts, dto.ExpeditionCostItemDTO{ Id: uint64(idx + 1), - ExpeditionVendorID: row.SupplierID, ExpeditionVendorName: row.SupplierName, - Qty: row.Qty, - UnitPrice: unitPrice, HPPAmount: row.TotalAmount, }) From f5c80fa560aafb6de4f5c22c3290e5e9d0e4fe00 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 18 Dec 2025 16:21:46 +0700 Subject: [PATCH 11/15] Feat(BE-339):Fixing dto reporting per supplier --- .../repports/dto/repportPurchase.dto.go | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/internal/modules/repports/dto/repportPurchase.dto.go b/internal/modules/repports/dto/repportPurchase.dto.go index 60fd0fee..830a076f 100644 --- a/internal/modules/repports/dto/repportPurchase.dto.go +++ b/internal/modules/repports/dto/repportPurchase.dto.go @@ -1,6 +1,7 @@ package dto import ( + "math" "time" entity "gitlab.com/mbugroup/lti-api.git/internal/entities" @@ -26,10 +27,12 @@ type PurchaseSupplierRowDTO struct { } type PurchaseSupplierSummaryDTO struct { - TotalQty float64 `json:"total_qty"` - TotalPurchaseValue float64 `json:"total_purchase_value"` - TotalTransportValue float64 `json:"total_transport_value"` - TotalAmount float64 `json:"total_amount"` + TotalQty float64 `json:"total_qty"` + TotalPurchaseValue float64 `json:"total_purchase_value"` + TotalTransportValue float64 `json:"total_transport_value"` + TotalAmount float64 `json:"total_amount"` + TotalUnitPrice float64 `json:"total_unit_price"` + TotalTransportUnitPrice float64 `json:"total_transport_unit_price"` } type PurchaseSupplierDTO struct { @@ -119,6 +122,11 @@ func ToPurchaseSupplierDTO(supplier entity.Supplier, items []entity.PurchaseItem rows := make([]PurchaseSupplierRowDTO, 0, len(items)) summary := PurchaseSupplierSummaryDTO{} + var unitPriceSum float64 + var unitPriceCount int + var transportUnitPriceSum float64 + var transportUnitPriceCount int + for i := range items { row := ToPurchaseSupplierRowDTO(&items[i]) rows = append(rows, row) @@ -127,6 +135,20 @@ func ToPurchaseSupplierDTO(supplier entity.Supplier, items []entity.PurchaseItem summary.TotalPurchaseValue += row.PurchaseValue summary.TotalTransportValue += row.TransportValue summary.TotalAmount += row.TotalAmount + + unitPriceSum += row.UnitPrice + unitPriceCount++ + + transportUnitPriceSum += row.TransportUnitPrice + transportUnitPriceCount++ + } + + if unitPriceCount > 0 { + summary.TotalUnitPrice = math.Round(unitPriceSum / float64(unitPriceCount)) + } + + if transportUnitPriceCount > 0 { + summary.TotalTransportUnitPrice = math.Round(transportUnitPriceSum / float64(transportUnitPriceCount)) } return PurchaseSupplierDTO{ @@ -135,4 +157,3 @@ func ToPurchaseSupplierDTO(supplier entity.Supplier, items []entity.PurchaseItem Summary: summary, } } - From cb076d92ace5a7e3eb666dd783a9a83573bfc6b6 Mon Sep 17 00:00:00 2001 From: ragilap Date: Thu, 18 Dec 2025 16:41:56 +0700 Subject: [PATCH 12/15] Feat(BE-339):Fixing dto reporting per supplier, and adjust limit --- internal/modules/repports/validations/repport.validation.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/modules/repports/validations/repport.validation.go b/internal/modules/repports/validations/repport.validation.go index 53ba22d7..a69e7716 100644 --- a/internal/modules/repports/validations/repport.validation.go +++ b/internal/modules/repports/validations/repport.validation.go @@ -30,7 +30,7 @@ type MarketingQuery struct { type PurchaseSupplierQuery struct { Page int `query:"page" validate:"omitempty,min=1,gt=0"` - Limit int `query:"limit" validate:"omitempty,min=1,max=100,gt=0"` + Limit int `query:"limit" validate:"omitempty,min=1,gt=0"` AreaId int64 `query:"area_id" validate:"omitempty"` SupplierId int64 `query:"supplier_id" validate:"omitempty"` ProductId int64 `query:"product_id" validate:"omitempty"` From 207382b3b0f5842478cd9d52e4373a61873f3785 Mon Sep 17 00:00:00 2001 From: MacBook Air M1 Date: Fri, 19 Dec 2025 07:05:11 +0700 Subject: [PATCH 13/15] fix get all inventory product stock --- .../product-stocks/services/product-stock.service.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/modules/inventory/product-stocks/services/product-stock.service.go b/internal/modules/inventory/product-stocks/services/product-stock.service.go index a0765d84..11475109 100644 --- a/internal/modules/inventory/product-stocks/services/product-stock.service.go +++ b/internal/modules/inventory/product-stocks/services/product-stock.service.go @@ -64,11 +64,18 @@ func (s productStockService) GetAll(c *fiber.Ctx, params *validation.Query) ([]e offset := (params.Page - 1) * params.Limit productStocks, total, err := s.ProductRepository.GetAll(c.Context(), offset, params.Limit, func(db *gorm.DB) *gorm.DB { + db = db.Where(`EXISTS ( + SELECT 1 + FROM product_warehouses pw + WHERE pw.product_id = products.id + AND pw.qty > 0 + )`) + db = s.withRelations(db) if params.Search != "" { - return db.Where("name ILIKE ?", "%"+params.Search+"%") + db = db.Where("products.name ILIKE ?", "%"+params.Search+"%") } - return db.Order("created_at DESC").Order("updated_at DESC") + return db.Order("products.created_at DESC").Order("products.updated_at DESC") }) if err != nil { From ab9c7c216aad8f7d3e2bbf1d41a1a52dc6d0b28e Mon Sep 17 00:00:00 2001 From: ragilap Date: Fri, 19 Dec 2025 14:37:54 +0700 Subject: [PATCH 14/15] Feat(BE-304): add permission in report and closing --- internal/capabilities/capabilities.go | 44 ------ internal/middleware/permissions.go | 220 ++++++++++++++------------ internal/modules/closings/route.go | 11 +- internal/modules/repports/module.go | 7 +- internal/modules/repports/route.go | 9 +- 5 files changed, 133 insertions(+), 158 deletions(-) delete mode 100644 internal/capabilities/capabilities.go diff --git a/internal/capabilities/capabilities.go b/internal/capabilities/capabilities.go deleted file mode 100644 index 47f774ba..00000000 --- a/internal/capabilities/capabilities.go +++ /dev/null @@ -1,44 +0,0 @@ -package capabilities - -import ( - "strings" - - permission "gitlab.com/mbugroup/lti-api.git/internal/middleware" -) - -// FromPermissions returns a filtered map of capabilities that the frontend can use -// to toggle features. Only permissions recognized by the application are exposed. -func FromPermissions(perms []string) map[string]bool { - if len(perms) == 0 { - return nil - } - - out := make(map[string]bool) - for _, perm := range perms { - if key, ok := normalizeAndAllow(perm); ok { - out[key] = true - } - } - if len(out) == 0 { - return nil - } - return out -} - -func normalizeAndAllow(perm string) (string, bool) { - perm = strings.ToLower(strings.TrimSpace(perm)) - if perm == "" { - return "", false - } - if _, ok := allowed[perm]; !ok { - return "", false - } - return perm, true -} - -var allowed = map[string]struct{}{ - permission.PermissionRecordingRead: {}, - permission.PermissionRecordingCreate: {}, - permission.PermissionRecordingUpdate: {}, - permission.PermissionRecordingDelete: {}, -} diff --git a/internal/middleware/permissions.go b/internal/middleware/permissions.go index 0734b035..462bc8b7 100644 --- a/internal/middleware/permissions.go +++ b/internal/middleware/permissions.go @@ -1,183 +1,197 @@ package middleware -//project-flock +// project-flock const ( P_ProjectFlockKandangsClosing = "lti.production.project_flock_kandangs.closing" - P_ProjectFlockKandangsGetAll = "lti.production.project_flock_kandangs.list" - P_ProjectFlockKandangsGetOne = "lti.production.project_flock_kandangs.detail" + P_ProjectFlockKandangsGetAll = "lti.production.project_flock_kandangs.list" + P_ProjectFlockKandangsGetOne = "lti.production.project_flock_kandangs.detail" - P_ProjectFlockGetAll = "lti.production.project_flocks.list" - P_ProjectFlockCreate = "lti.production.project_flocks.create" - P_ProjectFlockGetOne = "lti.production.project_flocks.detail" - P_ProjectFlockUpdate = "lti.production.project_flocks.update" - P_ProjectFlockDelete = "lti.production.project_flocks.delete" - P_ProjectFlockApprove = "lti.production.project_flocks.approve" - P_ProjectFlockLookup = "lti.production.project_flocks.lookup" + P_ProjectFlockGetAll = "lti.production.project_flocks.list" + P_ProjectFlockCreate = "lti.production.project_flocks.create" + P_ProjectFlockGetOne = "lti.production.project_flocks.detail" + P_ProjectFlockUpdate = "lti.production.project_flocks.update" + P_ProjectFlockDelete = "lti.production.project_flocks.delete" + P_ProjectFlockApprove = "lti.production.project_flocks.approve" + P_ProjectFlockLookup = "lti.production.project_flocks.lookup" P_ProjectFlockNextPeriod = "lti.production.project_flocks.next_period" - P_ProjectFlockResubmit = "lti.production.project_flocks.resubmit" + P_ProjectFlockResubmit = "lti.production.project_flocks.resubmit" ) -const( - P_ExpenseGetAll= "lti.expense.list" - P_ExpenseCreateOne= "lti.expense.create" - P_ExpenseUpdateOne= "lti.expense.update" - P_ExpenseGetOne= "lti.expense.detail" - P_ExpenseDeleteOne= "lti.expense.delete" - P_ExpenseApprovalManager= "lti.expense.approve.manager" - P_ExpenseApprovalFinance= "lti.expense.approve.finance" - P_ExpenseCreateRealizations= "lti.expense.create.realization" - P_ExpenseUpdateRealizations= "lti.expense.update.realization" - P_ExpenseCompleteExpense= "lti.expense.complete.expense" - P_ExpenseDocument= "lti.expense.document" - P_ExpenseDocumentRealizations= "lti.expense.document.realization" +const ( + P_ExpenseGetAll = "lti.expense.list" + P_ExpenseCreateOne = "lti.expense.create" + P_ExpenseUpdateOne = "lti.expense.update" + P_ExpenseGetOne = "lti.expense.detail" + P_ExpenseDeleteOne = "lti.expense.delete" + P_ExpenseApprovalManager = "lti.expense.approve.manager" + P_ExpenseApprovalFinance = "lti.expense.approve.finance" + P_ExpenseCreateRealizations = "lti.expense.create.realization" + P_ExpenseUpdateRealizations = "lti.expense.update.realization" + P_ExpenseCompleteExpense = "lti.expense.complete.expense" + P_ExpenseDocument = "lti.expense.document" + P_ExpenseDocumentRealizations = "lti.expense.document.realization" ) -const( - P_AdjustmentGetAll="lti.inventory.list" - P_AdjustmentCreate="lti.inventory.create" - P_AdjustmentGetOne="lti.inventory.detail" +const ( + P_AdjustmentGetAll = "lti.inventory.list" + P_AdjustmentCreate = "lti.inventory.create" + P_AdjustmentGetOne = "lti.inventory.detail" ) -const( +const ( P_ApprovalGetAll = "lti.approval.list" ) - -const( - P_ClosingGetAll = "lti.closing.list" - P_ClosingPenjualan = "lti.closing.penjualan" - P_ClosingGetSummary = "lti.closing.getsummary" - P_ProductStockGetAll = "lti.inventory.product_stock.list" - P_ProductStockGetOne = "lti.inventory.product_stock.detail" - P_ProductWarehousekGetAll = "lti.inventory.product_warehouses.list" - P_ProductWarehouseGetOne = "lti.inventory.product_warehouses.detail" +const ( + P_ReportExpenseGetAll = "lti.repport.expense.list" + P_ReportDeliveryGetAll = "lti.repport.delivery.list" ) -const( - P_TransferGetAll = "lti.inventory.transfer.list" - P_TransferGetOne = "lti.inventory.transfer.detail" + +const ( + P_ProductStockGetAll = "lti.inventory.product_stock.list" + P_ProductStockGetOne = "lti.inventory.product_stock.detail" + P_ProductWarehousekGetAll = "lti.inventory.product_warehouses.list" + P_ProductWarehouseGetOne = "lti.inventory.product_warehouses.detail" +) +const ( + P_ClosingGetAll = "lti.closing.list" + P_ClosingPenjualan = "lti.closing.penjualan" + P_ClosingGetSummary = "lti.closing.getsummary" + + + //?baru + P_ClosingGetOverhead = "lti.closing.getoverhead" + P_ClosingCountSapronakKandang = "lti.closing.getsapronakcountbykandang" + P_ClosingCountSapronak = "lti.closing.getsapronakcount" + P_ClosingSapronak = "lti.closing.getsapronak" + +) + +const ( + P_TransferGetAll = "lti.inventory.transfer.list" + P_TransferGetOne = "lti.inventory.transfer.detail" P_TransferCreateOne = "lti.inventory.transfer.create" ) -const( - P_DeliveryGetAll = "lti.marketing.delivery_order.list" - P_DeliveryGetOne = "lti.marketing.delivery_order.detail" - P_DeliveryCreateOne = "lti.marketing.delivery_order.create" - P_DeliveryUpdateOne = "lti.marketing.delivery_order.update" - P_SalesOrderDelete = "lti.marketing.sales_order.delete" - P_SalesOrderApproval = "lti.marketing.sales_order.approve" +const ( + P_DeliveryGetAll = "lti.marketing.delivery_order.list" + P_DeliveryGetOne = "lti.marketing.delivery_order.detail" + P_DeliveryCreateOne = "lti.marketing.delivery_order.create" + P_DeliveryUpdateOne = "lti.marketing.delivery_order.update" + P_SalesOrderDelete = "lti.marketing.sales_order.delete" + P_SalesOrderApproval = "lti.marketing.sales_order.approve" P_SalesOrderCreateOne = "lti.marketing.sales_order.create" P_SalesOrderUpdateOne = "lti.marketing.sales_order.update" ) -const( - P_AreaGetAll = "lti.master.area.list" - P_AreaGetOne = "lti.master.area.detail" +const ( + P_AreaGetAll = "lti.master.area.list" + P_AreaGetOne = "lti.master.area.detail" P_AreaCreateOne = "lti.master.area.create" P_AreaUpdateOne = "lti.master.area.update" P_AreaDeleteOne = "lti.master.area.delete" - P_BanksGetAll = "lti.master.banks.list" - P_BanksGetOne = "lti.master.banks.detail" + P_BanksGetAll = "lti.master.banks.list" + P_BanksGetOne = "lti.master.banks.detail" P_BanksCreateOne = "lti.master.banks.create" P_BanksUpdateOne = "lti.master.banks.update" P_BanksDeleteOne = "lti.master.banks.delete" - P_CustomerGetAll = "lti.master.customer.list" - P_CustomerGetOne = "lti.master.customer.detail" + P_CustomerGetAll = "lti.master.customer.list" + P_CustomerGetOne = "lti.master.customer.detail" P_CustomerCreateOne = "lti.master.customer.create" P_CustomerUpdateOne = "lti.master.customer.update" P_CustomerDeleteOne = "lti.master.customer.delete" - - P_FcrGetAll = "lti.master.fcr.list" - P_FcrGetOne = "lti.master.fcr.detail" + + P_FcrGetAll = "lti.master.fcr.list" + P_FcrGetOne = "lti.master.fcr.detail" P_FcrCreateOne = "lti.master.fcr.create" P_FcrUpdateOne = "lti.master.fcr.update" P_FcrDeleteOne = "lti.master.fcr.delete" - - P_FlocksGetAll = "lti.master.flocks.list" - P_FlocksGetOne = "lti.master.flocks.detail" + + P_FlocksGetAll = "lti.master.flocks.list" + P_FlocksGetOne = "lti.master.flocks.detail" P_FlocksCreateOne = "lti.master.flocks.create" P_FlocksUpdateOne = "lti.master.flocks.update" P_FlocksDeleteOne = "lti.master.flocks.delete" - - P_KandangsGetAll = "lti.master.kandangs.list" - P_KandangsGetOne = "lti.master.kandangs.detail" + + P_KandangsGetAll = "lti.master.kandangs.list" + P_KandangsGetOne = "lti.master.kandangs.detail" P_KandangsCreateOne = "lti.master.kandangs.create" P_KandangsUpdateOne = "lti.master.kandangs.update" P_KandangsDeleteOne = "lti.master.kandangs.delete" - - P_LocationsGetAll = "lti.master.locations.list" - P_LocationsGetOne = "lti.master.locations.detail" + + P_LocationsGetAll = "lti.master.locations.list" + P_LocationsGetOne = "lti.master.locations.detail" P_LocationsCreateOne = "lti.master.locations.create" P_LocationsUpdateOne = "lti.master.locations.update" P_LocationsDeleteOne = "lti.master.locations.delete" - - P_NonstocksGetAll = "lti.master.nonstocks.list" - P_NonstocksGetOne = "lti.master.nonstocks.detail" + + P_NonstocksGetAll = "lti.master.nonstocks.list" + P_NonstocksGetOne = "lti.master.nonstocks.detail" P_NonstocksCreateOne = "lti.master.nonstocks.create" P_NonstocksUpdateOne = "lti.master.nonstocks.update" P_NonstocksDeleteOne = "lti.master.nonstocks.delete" - P_ProductCategoriesGetAll = "lti.master.Product_categories.list" - P_ProductCategoriesGetOne = "lti.master.Product_categories.detail" + P_ProductCategoriesGetAll = "lti.master.Product_categories.list" + P_ProductCategoriesGetOne = "lti.master.Product_categories.detail" P_ProductCategoriesCreateOne = "lti.master.Product_categories.create" P_ProductCategoriesUpdateOne = "lti.master.Product_categories.update" P_ProductCategoriesDeleteOne = "lti.master.Product_categories.delete" - - P_ProductsGetAll = "lti.master.Products.list" - P_ProductsGetOne = "lti.master.Products.detail" + + P_ProductsGetAll = "lti.master.Products.list" + P_ProductsGetOne = "lti.master.Products.detail" P_ProductsCreateOne = "lti.master.Products.create" P_ProductsUpdateOne = "lti.master.Products.update" P_ProductsDeleteOne = "lti.master.Products.delete" - - P_SuppliersGetAll = "lti.master.suppliers.list" - P_SuppliersGetOne = "lti.master.suppliers.detail" + + P_SuppliersGetAll = "lti.master.suppliers.list" + P_SuppliersGetOne = "lti.master.suppliers.detail" P_SuppliersCreateOne = "lti.master.suppliers.create" P_SuppliersUpdateOne = "lti.master.suppliers.update" P_SuppliersDeleteOne = "lti.master.suppliers.delete" - P_UomsGetAll = "lti.master.uoms.list" - P_UomsGetOne = "lti.master.uoms.detail" + P_UomsGetAll = "lti.master.uoms.list" + P_UomsGetOne = "lti.master.uoms.detail" P_UomsCreateOne = "lti.master.uoms.create" P_UomsUpdateOne = "lti.master.uoms.update" P_UomsDeleteOne = "lti.master.uoms.delete" - P_WarehousesGetAll = "lti.master.warehouses.list" - P_WarehousesGetOne = "lti.master.warehouses.detail" + P_WarehousesGetAll = "lti.master.warehouses.list" + P_WarehousesGetOne = "lti.master.warehouses.detail" P_WarehousesCreateOne = "lti.master.warehouses.create" P_WarehousesUpdateOne = "lti.master.warehouses.update" P_WarehousesDeleteOne = "lti.master.warehouses.delete" - ) - -const( +const ( P_ChickinsCreateOne = "lti.production.chickins.create" - P_ChickinsGetOne = "lti.production.chickins.detail" - P_ChickinsApproval = "lti.production.chickins.approve" + P_ChickinsGetOne = "lti.production.chickins.detail" + P_ChickinsApproval = "lti.production.chickins.approve" ) -//recording + +// recording const ( - P_RecordingGetAll = "lti.production.recording.list" - P_RecordingGetOne = "lti.production.recording.detail" - P_RecordingCreateOne = "lti.production.recording.create" - P_RecordingUpdateOne = "lti.production.recording.update" - P_RecordingDeleteOne = "lti.production.recording.delete" + P_RecordingGetAll = "lti.production.recording.list" + P_RecordingGetOne = "lti.production.recording.detail" + P_RecordingCreateOne = "lti.production.recording.create" + P_RecordingUpdateOne = "lti.production.recording.update" + P_RecordingDeleteOne = "lti.production.recording.delete" P_RecordingNextDay = "lti.production.recording.next_day" - P_RecordingApproval = "lti.production.recording.approve" + P_RecordingApproval = "lti.production.recording.approve" ) const ( - P_PurchaseGetAll = "lti.Purchase.list" - P_PurchaseGetOne = "lti.Purchase.detail" - P_PurchaseCreateOne = "lti.Purchase.create" - P_PurchaseUpdateOne = "lti.Purchase.update" - P_PurchaseDeleteOne = "lti.Purchase.delete" + P_PurchaseGetAll = "lti.Purchase.list" + P_PurchaseGetOne = "lti.Purchase.detail" + P_PurchaseCreateOne = "lti.Purchase.create" + P_PurchaseUpdateOne = "lti.Purchase.update" + P_PurchaseDeleteOne = "lti.Purchase.delete" P_PurchaseItemDeleteOne = "lti.Purchase.delete.item" - P_PurchaseReceive = "lti.Purchase.receive" + P_PurchaseReceive = "lti.Purchase.receive" P_PurchaseApprovalStaff = "lti.Purchase.approve.staff" - P_PurchaseApprovalManager = "lti.Purchase.approve.manager" + P_PurchaseApprovalManager = "lti.Purchase.approve.manager" ) -const( +const ( P_UserGetAll = "lti.users.list" P_UserGetOne = "lti.users.detail" -) \ No newline at end of file +) diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 5033f989..38f8a816 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -24,11 +24,8 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService route.Get("/",m.RequirePermissions(m.P_ClosingGetAll), ctrl.GetAll) route.Get("/:project_flock_id/penjualan",m.RequirePermissions(m.P_ClosingPenjualan), ctrl.GetPenjualan) route.Get("/:projectFlockId",m.RequirePermissions(m.P_ClosingGetSummary), ctrl.GetClosingSummary) - route.Get("/", ctrl.GetAll) - route.Get("/:project_flock_id/penjualan", ctrl.GetPenjualan) - route.Get("/:project_flock_id/overhead", ctrl.GetOverhead) - route.Get("/:project_flock_id/:project_flock_kandang_id/perhitungan_sapronak", ctrl.GetSapronakByKandang) - route.Get("/:project_flock_id/perhitungan_sapronak", ctrl.GetSapronakByProject) - route.Get("/:projectFlockId", ctrl.GetClosingSummary) - route.Get("/:projectFlockId/sapronak", ctrl.GetClosingSapronak) + route.Get("/:project_flock_id/overhead",m.RequirePermissions(m.P_ClosingGetOverhead), ctrl.GetOverhead) + route.Get("/:project_flock_id/:project_flock_kandang_id/perhitungan_sapronak",m.RequirePermissions(m.P_ClosingCountSapronakKandang) ,ctrl.GetSapronakByKandang) + route.Get("/:project_flock_id/perhitungan_sapronak",m.RequirePermissions(m.P_ClosingCountSapronak) ,ctrl.GetSapronakByProject) + route.Get("/:projectFlockId/sapronak",m.RequirePermissions(m.P_ClosingSapronak), ctrl.GetClosingSapronak) } diff --git a/internal/modules/repports/module.go b/internal/modules/repports/module.go index 4479b733..c1a00e8c 100644 --- a/internal/modules/repports/module.go +++ b/internal/modules/repports/module.go @@ -11,6 +11,9 @@ import ( expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories" marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories" + + rUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/repositories" + sUser "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" ) type RepportModule struct{} @@ -20,9 +23,11 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate * expenseRealizationRepository := expenseRepo.NewExpenseRealizationRepository(db) marketingDeliveryProductRepository := marketingRepo.NewMarketingDeliveryProductRepository(db) approvalRepository := commonRepo.NewApprovalRepository(db) + userRepository := rUser.NewUserRepository(db) approvalSvc := approvalService.NewApprovalService(approvalRepository) repportService := sRepport.NewRepportService(validate, expenseRealizationRepository, marketingDeliveryProductRepository, approvalSvc) + userService := sUser.NewUserService(userRepository, validate) - RepportRoutes(router, repportService) + RepportRoutes(router, userService, repportService) } diff --git a/internal/modules/repports/route.go b/internal/modules/repports/route.go index 4aea831c..4edba9c7 100644 --- a/internal/modules/repports/route.go +++ b/internal/modules/repports/route.go @@ -1,17 +1,20 @@ package repports import ( + m "gitlab.com/mbugroup/lti-api.git/internal/middleware" controller "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/controllers" repport "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/services" + user "gitlab.com/mbugroup/lti-api.git/internal/modules/users/services" "github.com/gofiber/fiber/v2" ) -func RepportRoutes(v1 fiber.Router, s repport.RepportService) { +func RepportRoutes(v1 fiber.Router, u user.UserService, s repport.RepportService) { ctrl := controller.NewRepportController(s) route := v1.Group("/reports") + route.Use(m.Auth(u)) - route.Get("/expense", ctrl.GetExpense) - route.Get("/marketing", ctrl.GetMarketing) + route.Get("/expense", m.RequirePermissions(m.P_ReportExpenseGetAll), ctrl.GetExpense) + route.Get("/marketing", m.RequirePermissions(m.P_ReportDeliveryGetAll), ctrl.GetMarketing) } From 1af8f0a72600430e6f80932bc84e6ddd4a2e4348 Mon Sep 17 00:00:00 2001 From: ragilap Date: Fri, 19 Dec 2025 15:55:30 +0700 Subject: [PATCH 15/15] Feat(BE-304): add permission in report and closing --- internal/middleware/permissions.go | 10 ++++++---- internal/modules/closings/route.go | 20 ++++++++++---------- internal/modules/repports/route.go | 2 +- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/internal/middleware/permissions.go b/internal/middleware/permissions.go index 462bc8b7..e715aae9 100644 --- a/internal/middleware/permissions.go +++ b/internal/middleware/permissions.go @@ -42,6 +42,7 @@ const ( const ( P_ReportExpenseGetAll = "lti.repport.expense.list" P_ReportDeliveryGetAll = "lti.repport.delivery.list" + P_ReportPurchaseSupplierGetAll = "lti.repport.purchasesupplier.list" ) @@ -55,14 +56,15 @@ const ( P_ClosingGetAll = "lti.closing.list" P_ClosingPenjualan = "lti.closing.penjualan" P_ClosingGetSummary = "lti.closing.getsummary" - - - //?baru P_ClosingGetOverhead = "lti.closing.getoverhead" - P_ClosingCountSapronakKandang = "lti.closing.getsapronakcountbykandang" + P_ClosingCountSapronakKandang = "lti.closing.getsapronakcount.kandang" P_ClosingCountSapronak = "lti.closing.getsapronakcount" P_ClosingSapronak = "lti.closing.getsapronak" + P_ClosingExpeditionHpp = "lti.closing.expedition" + P_ClosingExpeditionHppByKandang = "lti.closing.expedition.kandang" + P_ClosingDataProduction = "lti.closing.production.data" + ) const ( diff --git a/internal/modules/closings/route.go b/internal/modules/closings/route.go index 58372183..d4250624 100644 --- a/internal/modules/closings/route.go +++ b/internal/modules/closings/route.go @@ -21,14 +21,14 @@ func ClosingRoutes(v1 fiber.Router, u user.UserService, s closing.ClosingService // route.Patch("/:id", m.Auth(u), ctrl.UpdateOne) // route.Delete("/:id", m.Auth(u), ctrl.DeleteOne) - route.Get("/",m.RequirePermissions(m.P_ClosingGetAll), ctrl.GetAll) - route.Get("/:project_flock_id/penjualan",m.RequirePermissions(m.P_ClosingPenjualan), ctrl.GetPenjualan) - route.Get("/:projectFlockId",m.RequirePermissions(m.P_ClosingGetSummary), ctrl.GetClosingSummary) - route.Get("/:project_flock_id/overhead",m.RequirePermissions(m.P_ClosingGetOverhead), ctrl.GetOverhead) - route.Get("/:project_flock_id/:project_flock_kandang_id/perhitungan_sapronak",m.RequirePermissions(m.P_ClosingCountSapronakKandang) ,ctrl.GetSapronakByKandang) - route.Get("/:project_flock_id/perhitungan_sapronak",m.RequirePermissions(m.P_ClosingCountSapronak) ,ctrl.GetSapronakByProject) - route.Get("/:projectFlockId/sapronak",m.RequirePermissions(m.P_ClosingSapronak), ctrl.GetClosingSapronak) - route.Get("/:project_flock_id/expedition-hpp", ctrl.GetExpeditionHPP) - route.Get("/:project_flock_id/:project_flock_kandang_id/expedition-hpp", ctrl.GetExpeditionHPPByKandang) - route.Get("/:projectFlockId/data-produksi", ctrl.GetClosingDataProduksi) + route.Get("/", m.RequirePermissions(m.P_ClosingGetAll), ctrl.GetAll) + route.Get("/:project_flock_id/penjualan", m.RequirePermissions(m.P_ClosingPenjualan), ctrl.GetPenjualan) + route.Get("/:projectFlockId", m.RequirePermissions(m.P_ClosingGetSummary), ctrl.GetClosingSummary) + route.Get("/:project_flock_id/overhead", m.RequirePermissions(m.P_ClosingGetOverhead), ctrl.GetOverhead) + route.Get("/:project_flock_id/:project_flock_kandang_id/perhitungan_sapronak", m.RequirePermissions(m.P_ClosingCountSapronakKandang), ctrl.GetSapronakByKandang) + route.Get("/:project_flock_id/perhitungan_sapronak", m.RequirePermissions(m.P_ClosingCountSapronak), ctrl.GetSapronakByProject) + route.Get("/:projectFlockId/sapronak", m.RequirePermissions(m.P_ClosingSapronak), ctrl.GetClosingSapronak) + route.Get("/:project_flock_id/expedition-hpp", m.RequirePermissions(m.P_ClosingExpeditionHpp), ctrl.GetExpeditionHPP) + route.Get("/:project_flock_id/:project_flock_kandang_id/expedition-hpp", m.RequirePermissions(m.P_ClosingExpeditionHppByKandang), ctrl.GetExpeditionHPPByKandang) + route.Get("/:projectFlockId/data-produksi", m.RequirePermissions(m.P_ClosingDataProduction), ctrl.GetClosingDataProduksi) } diff --git a/internal/modules/repports/route.go b/internal/modules/repports/route.go index 93758f07..45dc32b7 100644 --- a/internal/modules/repports/route.go +++ b/internal/modules/repports/route.go @@ -17,5 +17,5 @@ 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", ctrl.GetPurchaseSupplier) + route.Get("/purchase-supplier", m.RequirePermissions(m.P_ReportPurchaseSupplierGetAll), ctrl.GetPurchaseSupplier) }