mirror of
https://gitlab.com/mbugroup/lti-api.git
synced 2026-05-24 07:15:43 +00:00
Merge branch 'development' into 'staging'
Development See merge request mbugroup/lti-api!196
This commit is contained in:
@@ -52,6 +52,7 @@ const (
|
|||||||
P_ReportDebtSupplierGetAll = "lti.repport.debtsupplier.list"
|
P_ReportDebtSupplierGetAll = "lti.repport.debtsupplier.list"
|
||||||
P_ReportHppPerKandangGetAll = "lti.repport.gethppperkandang.list"
|
P_ReportHppPerKandangGetAll = "lti.repport.gethppperkandang.list"
|
||||||
P_ReportProductionResultGetAll = "lti.repport.production_result.list"
|
P_ReportProductionResultGetAll = "lti.repport.production_result.list"
|
||||||
|
P_ReportCustomerPaymentGetAll = "lti.repport.customerpayment.list"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ func (s *deliveryOrdersService) CreateOne(c *fiber.Ctx, req *validation.Delivery
|
|||||||
|
|
||||||
// Hitung total_weight dan total_price otomatis
|
// Hitung total_weight dan total_price otomatis
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||||
totalPrice := requestedProduct.UnitPrice * requestedProduct.Qty
|
totalPrice := requestedProduct.UnitPrice * totalWeight
|
||||||
|
|
||||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||||
@@ -363,7 +363,7 @@ func (s deliveryOrdersService) UpdateOne(c *fiber.Ctx, req *validation.DeliveryO
|
|||||||
|
|
||||||
// Hitung total_weight dan total_price otomatis
|
// Hitung total_weight dan total_price otomatis
|
||||||
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
totalWeight := requestedProduct.Qty * requestedProduct.AvgWeight
|
||||||
totalPrice := requestedProduct.UnitPrice * requestedProduct.Qty
|
totalPrice := requestedProduct.UnitPrice * totalWeight
|
||||||
|
|
||||||
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
deliveryProduct.ProductWarehouseId = foundMarketingProduct.ProductWarehouseId
|
||||||
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
deliveryProduct.UnitPrice = requestedProduct.UnitPrice
|
||||||
|
|||||||
@@ -294,7 +294,7 @@ func (s salesOrdersService) UpdateOne(c *fiber.Ctx, req *validation.Update, id u
|
|||||||
|
|
||||||
// Hitung total_weight dan total_price otomatis
|
// Hitung total_weight dan total_price otomatis
|
||||||
totalWeight := rp.Qty * rp.AvgWeight
|
totalWeight := rp.Qty * rp.AvgWeight
|
||||||
totalPrice := rp.UnitPrice * rp.Qty
|
totalPrice := rp.UnitPrice * totalWeight
|
||||||
|
|
||||||
updateBody := map[string]any{
|
updateBody := map[string]any{
|
||||||
"product_warehouse_id": rp.ProductWarehouseId,
|
"product_warehouse_id": rp.ProductWarehouseId,
|
||||||
@@ -594,7 +594,7 @@ func (s *salesOrdersService) createMarketingProductWithDelivery(ctx context.Cont
|
|||||||
|
|
||||||
// Hitung total_weight dan total_price otomatis
|
// Hitung total_weight dan total_price otomatis
|
||||||
totalWeight := rp.Qty * rp.AvgWeight
|
totalWeight := rp.Qty * rp.AvgWeight
|
||||||
totalPrice := rp.UnitPrice * rp.Qty
|
totalPrice := rp.UnitPrice * totalWeight
|
||||||
|
|
||||||
marketingProduct := &entity.MarketingProduct{
|
marketingProduct := &entity.MarketingProduct{
|
||||||
MarketingId: marketingId,
|
MarketingId: marketingId,
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ type CustomerRelationDTO struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
AccountNumber string `json:"account_number"`
|
AccountNumber string `json:"account_number"`
|
||||||
|
Address string `json:"address,omitempty"`
|
||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
Pic *userDTO.UserRelationDTO `json:"pic,omitempty"`
|
Pic *userDTO.UserRelationDTO `json:"pic,omitempty"`
|
||||||
}
|
}
|
||||||
@@ -52,6 +53,8 @@ func ToCustomerRelationDTO(e entity.Customer) CustomerRelationDTO {
|
|||||||
Name: e.Name,
|
Name: e.Name,
|
||||||
Type: e.Type,
|
Type: e.Type,
|
||||||
AccountNumber: e.AccountNumber,
|
AccountNumber: e.AccountNumber,
|
||||||
|
Address: e.Address,
|
||||||
|
Balance: e.Balance,
|
||||||
Pic: pic,
|
Pic: pic,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,7 +584,6 @@ func (s chickinService) Approval(c *fiber.Ctx, req *validation.Approve) ([]entit
|
|||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// autoAddFlagToProduct adds target flag to product if not already present (idempotent)
|
|
||||||
func (s *chickinService) autoAddFlagToProduct(ctx context.Context, tx *gorm.DB, productID uint, targetFlag utils.FlagType) error {
|
func (s *chickinService) autoAddFlagToProduct(ctx context.Context, tx *gorm.DB, productID uint, targetFlag utils.FlagType) error {
|
||||||
if s.ProductRepo == nil {
|
if s.ProductRepo == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -242,6 +242,65 @@ func (c *RepportController) GetHppPerKandang(ctx *fiber.Ctx) error {
|
|||||||
return ctx.Status(fiber.StatusOK).JSON(resp)
|
return ctx.Status(fiber.StatusOK).JSON(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *RepportController) GetCustomerPayment(ctx *fiber.Ctx) error {
|
||||||
|
var customerIDs []uint
|
||||||
|
if customerIDsStr := ctx.Query("customer_ids"); customerIDsStr != "" {
|
||||||
|
ids := strings.Split(customerIDsStr, ",")
|
||||||
|
for _, idStr := range ids {
|
||||||
|
idStr = strings.TrimSpace(idStr)
|
||||||
|
if idStr != "" {
|
||||||
|
if id, err := strconv.ParseUint(idStr, 10, 32); err == nil {
|
||||||
|
customerIDs = append(customerIDs, uint(id))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &validation.CustomerPaymentQuery{
|
||||||
|
Page: ctx.QueryInt("page", 1),
|
||||||
|
Limit: ctx.QueryInt("limit", 10),
|
||||||
|
CustomerIDs: customerIDs,
|
||||||
|
StartDate: ctx.Query("start_date", ""),
|
||||||
|
EndDate: ctx.Query("end_date", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validate pagination
|
||||||
|
if len(customerIDs) == 0 && (query.Page < 1 || query.Limit < 1) {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "page and limit must be greater than 0 when customer_ids is not provided")
|
||||||
|
}
|
||||||
|
|
||||||
|
result, totalResults, err := c.RepportService.GetCustomerPayment(ctx, query)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// If single customer mode (only 1 customer ID), return without pagination
|
||||||
|
if len(customerIDs) == 1 {
|
||||||
|
return ctx.Status(fiber.StatusOK).
|
||||||
|
JSON(response.Success{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Get customer payment report successfully",
|
||||||
|
Data: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multiple customers mode with pagination
|
||||||
|
return ctx.Status(fiber.StatusOK).
|
||||||
|
JSON(response.SuccessWithPaginate[dto.CustomerPaymentReportItem]{
|
||||||
|
Code: fiber.StatusOK,
|
||||||
|
Status: "success",
|
||||||
|
Message: "Get customer payment report successfully",
|
||||||
|
Meta: response.Meta{
|
||||||
|
Page: query.Page,
|
||||||
|
Limit: query.Limit,
|
||||||
|
TotalPages: int64(math.Ceil(float64(totalResults) / float64(query.Limit))),
|
||||||
|
TotalResults: totalResults,
|
||||||
|
},
|
||||||
|
Data: result,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (c *RepportController) GetProductionResult(ctx *fiber.Ctx) error {
|
func (c *RepportController) GetProductionResult(ctx *fiber.Ctx) error {
|
||||||
idParam := ctx.Params("idProjectFlockKandang")
|
idParam := ctx.Params("idProjectFlockKandang")
|
||||||
if idParam == "" {
|
if idParam == "" {
|
||||||
|
|||||||
@@ -0,0 +1,120 @@
|
|||||||
|
package dto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitlab.com/mbugroup/lti-api.git/internal/entities"
|
||||||
|
customerDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/dto"
|
||||||
|
repportRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/repports/repositories"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomerPaymentReportRow struct {
|
||||||
|
TransactionType string `json:"transaction_type"`
|
||||||
|
TransactionID int64 `json:"transaction_id"`
|
||||||
|
TransDate time.Time `json:"trans_date"`
|
||||||
|
DeliveryDate *time.Time `json:"delivery_date"`
|
||||||
|
Reference string `json:"reference"`
|
||||||
|
|
||||||
|
Qty float64 `json:"qty"`
|
||||||
|
Weight float64 `json:"weight"`
|
||||||
|
AverageWeight float64 `json:"average_weight"`
|
||||||
|
UnitPrice float64 `json:"unit_price"`
|
||||||
|
FinalPrice float64 `json:"final_price"`
|
||||||
|
TotalPrice float64 `json:"total_price"`
|
||||||
|
PaymentAmount float64 `json:"payment_amount"`
|
||||||
|
AccountsReceivable float64 `json:"accounts_receivable"`
|
||||||
|
AgingDay *int `json:"aging_day"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
VehicleNumbers []string `json:"vehicle_numbers"`
|
||||||
|
PickupInfo []string `json:"pickup_info"`
|
||||||
|
SalesPerson string `json:"sales_person"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerPaymentReportSummary struct {
|
||||||
|
TotalQty float64 `json:"total_qty"`
|
||||||
|
TotalWeight float64 `json:"total_weight"`
|
||||||
|
TotalFinalAmount float64 `json:"total_final_amount"`
|
||||||
|
TotalGrandAmount float64 `json:"total_grand_amount"`
|
||||||
|
TotalPayment float64 `json:"total_payment"`
|
||||||
|
TotalAccountsReceivable float64 `json:"total_accounts_receivable"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerPaymentReportItem struct {
|
||||||
|
Customer customerDTO.CustomerRelationDTO `json:"customer"`
|
||||||
|
InitialBalance float64 `json:"initial_balance"`
|
||||||
|
Rows []CustomerPaymentReportRow `json:"rows"`
|
||||||
|
Summary CustomerPaymentReportSummary `json:"summary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerPaymentReportResponse struct {
|
||||||
|
Data []CustomerPaymentReportItem `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToCustomerPaymentReportRow(tx repportRepo.CustomerPaymentTransaction) CustomerPaymentReportRow {
|
||||||
|
return CustomerPaymentReportRow{
|
||||||
|
TransactionType: tx.TransactionType,
|
||||||
|
TransactionID: tx.TransactionID,
|
||||||
|
TransDate: tx.TransDate,
|
||||||
|
DeliveryDate: tx.DeliveryDate,
|
||||||
|
Reference: tx.Reference,
|
||||||
|
Qty: tx.Qty,
|
||||||
|
Weight: tx.Weight,
|
||||||
|
AverageWeight: tx.AverageWeight,
|
||||||
|
UnitPrice: tx.Price,
|
||||||
|
FinalPrice: tx.FinalPrice,
|
||||||
|
TotalPrice: tx.TotalPrice,
|
||||||
|
PaymentAmount: tx.PaymentAmount,
|
||||||
|
VehicleNumbers: parseStringSlice(tx.VehicleNumbers),
|
||||||
|
PickupInfo: parseStringSlice(tx.PickupInfo),
|
||||||
|
SalesPerson: tx.SalesPerson,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToCustomerPaymentReportItem(customer entities.Customer, initialBalance float64, rows []CustomerPaymentReportRow, summary CustomerPaymentReportSummary) CustomerPaymentReportItem {
|
||||||
|
return CustomerPaymentReportItem{
|
||||||
|
Customer: customerDTO.ToCustomerRelationDTO(customer),
|
||||||
|
InitialBalance: initialBalance,
|
||||||
|
Rows: rows,
|
||||||
|
Summary: summary,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ToCustomerPaymentReportSummary(rows []CustomerPaymentReportRow, initialBalance float64) CustomerPaymentReportSummary {
|
||||||
|
summary := CustomerPaymentReportSummary{}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
summary.TotalQty += row.Qty
|
||||||
|
summary.TotalWeight += row.Weight
|
||||||
|
|
||||||
|
if row.TransactionType == "SALES" {
|
||||||
|
summary.TotalFinalAmount += row.FinalPrice
|
||||||
|
summary.TotalGrandAmount += row.TotalPrice
|
||||||
|
} else if row.TransactionType == "PAYMENT" {
|
||||||
|
summary.TotalPayment += row.PaymentAmount
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Total AR = Initial Balance - Total Sales + Total Payment
|
||||||
|
summary.TotalAccountsReceivable = initialBalance - summary.TotalGrandAmount + summary.TotalPayment
|
||||||
|
|
||||||
|
return summary
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseStringSlice(str string) []string {
|
||||||
|
str = strings.TrimSpace(str)
|
||||||
|
if str == "" || str == "-" {
|
||||||
|
return []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(str, ",")
|
||||||
|
result := make([]string, 0, len(parts))
|
||||||
|
for _, part := range parts {
|
||||||
|
part = strings.TrimSpace(part)
|
||||||
|
if part != "" {
|
||||||
|
result = append(result, part)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -25,14 +25,12 @@ type HppPerKandangResponseData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HppPerKandangRowDTO struct {
|
type HppPerKandangRowDTO struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
Kandang HppPerKandangRowKandangDTO `json:"kandang"`
|
Kandang HppPerKandangRowKandangDTO `json:"kandang"`
|
||||||
WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"`
|
WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"`
|
||||||
RemainingChickenBirds int64 `json:"remaining_chicken_birds"`
|
AvgWeightKg float64 `json:"avg_weight_kg"`
|
||||||
RemainingChickenWeightKg float64 `json:"remaining_chicken_weight_kg"`
|
EggProductionPieces int64 `json:"egg_production_pieces"`
|
||||||
AvgWeightKg float64 `json:"avg_weight_kg"`
|
EggProductionKg float64 `json:"egg_production_kg"`
|
||||||
EggProductionPieces int64 `json:"egg_production_pieces"`
|
|
||||||
EggProductionKg float64 `json:"egg_production_kg"`
|
|
||||||
// FeedCostRp float64 `json:"feed_cost_rp"`
|
// FeedCostRp float64 `json:"feed_cost_rp"`
|
||||||
// OvkCostRp float64 `json:"ovk_cost_rp"`
|
// OvkCostRp float64 `json:"ovk_cost_rp"`
|
||||||
EggHppRpPerKg float64 `json:"egg_hpp_rp_per_kg"`
|
EggHppRpPerKg float64 `json:"egg_hpp_rp_per_kg"`
|
||||||
@@ -80,34 +78,28 @@ type HppPerKandangSummaryDTO struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type HppPerKandangSummaryWeightRangeDTO struct {
|
type HppPerKandangSummaryWeightRangeDTO struct {
|
||||||
ID int `json:"id"`
|
ID int `json:"id"`
|
||||||
WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"`
|
WeightRange HppPerKandangWeightRangeDTO `json:"weight_range"`
|
||||||
Label string `json:"label"`
|
Label string `json:"label"`
|
||||||
RemainingChickenBirds int64 `json:"remaining_chicken_birds"`
|
AvgWeightKg float64 `json:"avg_weight_kg"`
|
||||||
RemainingChickenWeightKg float64 `json:"remaining_chicken_weight_kg"`
|
EggProductionPieces int64 `json:"egg_production_pieces"`
|
||||||
AvgWeightKg float64 `json:"avg_weight_kg"`
|
EggProductionKg float64 `json:"egg_production_kg"`
|
||||||
EggProductionPieces int64 `json:"egg_production_pieces"`
|
EggHppRpPerKg float64 `json:"egg_hpp_rp_per_kg"`
|
||||||
EggProductionKg float64 `json:"egg_production_kg"`
|
EggValueRp int64 `json:"egg_value_rp"`
|
||||||
EggHppRpPerKg float64 `json:"egg_hpp_rp_per_kg"`
|
FeedSuppliers []HppPerKandangSupplierDTO `json:"feed_suppliers"`
|
||||||
EggValueRp int64 `json:"egg_value_rp"`
|
DocSuppliers []HppPerKandangSupplierDTO `json:"doc_suppliers"`
|
||||||
FeedSuppliers []HppPerKandangSupplierDTO `json:"feed_suppliers"`
|
AverageDocPriceRp float64 `json:"average_doc_price_rp"`
|
||||||
DocSuppliers []HppPerKandangSupplierDTO `json:"doc_suppliers"`
|
HppRp float64 `json:"hpp_rp"`
|
||||||
AverageDocPriceRp float64 `json:"average_doc_price_rp"`
|
RemainingValueRp int64 `json:"remaining_value_rp"`
|
||||||
HppRp float64 `json:"hpp_rp"`
|
|
||||||
RemainingValueRp int64 `json:"remaining_value_rp"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HppPerKandangSummaryTotalDTO struct {
|
type HppPerKandangSummaryTotalDTO struct {
|
||||||
TotalRemainingChickenBirds int64 `json:"total_remaining_chicken_birds"`
|
AverageWeightKg float64 `json:"average_weight_kg"`
|
||||||
TotalRemainingChickenWeightKg float64 `json:"total_remaining_chicken_weight_kg"`
|
TotalEggProductionPieces int64 `json:"total_egg_production_pieces"`
|
||||||
AverageWeightKg float64 `json:"average_weight_kg"`
|
TotalEggProductionKg float64 `json:"total_egg_production_kg"`
|
||||||
TotalRemainingValueRp int64 `json:"total_remaining_value_rp"`
|
AverageEggHppRpPerKg float64 `json:"average_egg_hpp_rp_per_kg"`
|
||||||
TotalEggProductionPieces int64 `json:"total_egg_production_pieces"`
|
TotalEggValueRp int64 `json:"total_egg_value_rp"`
|
||||||
TotalEggProductionKg float64 `json:"total_egg_production_kg"`
|
TotalAverageDocPriceRp float64 `json:"total_average_doc_price_rp"`
|
||||||
AverageEggHppRpPerKg float64 `json:"average_egg_hpp_rp_per_kg"`
|
|
||||||
TotalEggValueRp int64 `json:"total_egg_value_rp"`
|
|
||||||
TotalHppRp float64 `json:"total_hpp_rp"`
|
|
||||||
TotalAverageDocPriceRp float64 `json:"total_average_doc_price_rp"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHppPerKandangFiltersDTO(area, location, kandang, weightMin, weightMax, period, showUnrecorded string) HppPerKandangFiltersDTO {
|
func NewHppPerKandangFiltersDTO(area, location, kandang, weightMin, weightMax, period, showUnrecorded string) HppPerKandangFiltersDTO {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import (
|
|||||||
|
|
||||||
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||||
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
|
customerRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/repositories"
|
||||||
productionStandardRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
productionStandardRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
||||||
chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
chickinRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/chickins/repositories"
|
||||||
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
recordingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/production/recordings/repositories"
|
||||||
@@ -35,26 +36,14 @@ func (RepportModule) RegisterRoutes(router fiber.Router, db *gorm.DB, validate *
|
|||||||
debtSupplierRepository := repportRepo.NewDebtSupplierRepository(db)
|
debtSupplierRepository := repportRepo.NewDebtSupplierRepository(db)
|
||||||
hppPerKandangRepository := repportRepo.NewHppPerKandangRepository(db)
|
hppPerKandangRepository := repportRepo.NewHppPerKandangRepository(db)
|
||||||
productionResultRepository := repportRepo.NewProductionResultRepository(db)
|
productionResultRepository := repportRepo.NewProductionResultRepository(db)
|
||||||
|
customerPaymentRepository := repportRepo.NewCustomerPaymentRepository(db)
|
||||||
|
customerRepository := customerRepo.NewCustomerRepository(db)
|
||||||
standardGrowthDetailRepository := productionStandardRepo.NewStandardGrowthDetailRepository(db)
|
standardGrowthDetailRepository := productionStandardRepo.NewStandardGrowthDetailRepository(db)
|
||||||
productionStandardDetailRepository := productionStandardRepo.NewProductionStandardDetailRepository(db)
|
productionStandardDetailRepository := productionStandardRepo.NewProductionStandardDetailRepository(db)
|
||||||
userRepository := rUser.NewUserRepository(db)
|
userRepository := rUser.NewUserRepository(db)
|
||||||
|
|
||||||
approvalSvc := approvalService.NewApprovalService(approvalRepository)
|
approvalSvc := approvalService.NewApprovalService(approvalRepository)
|
||||||
repportService := sRepport.NewRepportService(
|
repportService := sRepport.NewRepportService(db, validate, expenseRealizationRepository, marketingDeliveryProductRepository, purchaseRepository, chickinRepository, recordingRepository, approvalSvc, purchaseSupplierRepository, debtSupplierRepository, hppPerKandangRepository, productionResultRepository, customerPaymentRepository, customerRepository, standardGrowthDetailRepository, productionStandardDetailRepository)
|
||||||
validate,
|
|
||||||
expenseRealizationRepository,
|
|
||||||
marketingDeliveryProductRepository,
|
|
||||||
purchaseRepository,
|
|
||||||
chickinRepository,
|
|
||||||
recordingRepository,
|
|
||||||
approvalSvc,
|
|
||||||
purchaseSupplierRepository,
|
|
||||||
debtSupplierRepository,
|
|
||||||
hppPerKandangRepository,
|
|
||||||
productionResultRepository,
|
|
||||||
standardGrowthDetailRepository,
|
|
||||||
productionStandardDetailRepository,
|
|
||||||
)
|
|
||||||
userService := sUser.NewUserService(userRepository, validate)
|
userService := sUser.NewUserService(userRepository, validate)
|
||||||
|
|
||||||
RepportRoutes(router, userService, repportService)
|
RepportRoutes(router, userService, repportService)
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
package repositories
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CustomerPaymentTransaction struct {
|
||||||
|
TransactionType string `gorm:"column:transaction_type"`
|
||||||
|
TransactionID int64 `gorm:"column:transaction_id"`
|
||||||
|
CustomerID int64 `gorm:"column:customer_id"`
|
||||||
|
TransDate time.Time `gorm:"column:trans_date"`
|
||||||
|
DeliveryDate *time.Time `gorm:"column:delivery_date"`
|
||||||
|
Reference string `gorm:"column:reference"`
|
||||||
|
VehicleNumbers string `gorm:"column:vehicle_numbers"`
|
||||||
|
Qty float64 `gorm:"column:qty"`
|
||||||
|
Weight float64 `gorm:"column:weight"`
|
||||||
|
AverageWeight float64 `gorm:"column:average_weight"`
|
||||||
|
Price float64 `gorm:"column:price"`
|
||||||
|
FinalPrice float64 `gorm:"column:final_price"`
|
||||||
|
TotalPrice float64 `gorm:"column:total_price"`
|
||||||
|
PaymentAmount float64 `gorm:"column:payment_amount"`
|
||||||
|
PickupInfo string `gorm:"column:pickup_info"`
|
||||||
|
SalesPerson string `gorm:"column:sales_person"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CustomerPaymentRepository interface {
|
||||||
|
GetCustomerPaymentTransactions(ctx context.Context, customerID *uint) ([]CustomerPaymentTransaction, error)
|
||||||
|
GetInitialBalanceByCustomer(ctx context.Context, customerID uint) (float64, error)
|
||||||
|
GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int) ([]uint, int64, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type customerPaymentRepositoryImpl struct {
|
||||||
|
db *gorm.DB
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCustomerPaymentRepository(db *gorm.DB) CustomerPaymentRepository {
|
||||||
|
return &customerPaymentRepositoryImpl{db: db}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *customerPaymentRepositoryImpl) GetCustomerPaymentTransactions(ctx context.Context, customerID *uint) ([]CustomerPaymentTransaction, error) {
|
||||||
|
salesQuery := r.db.WithContext(ctx).
|
||||||
|
Table("marketing_delivery_products mdp").
|
||||||
|
Select(`
|
||||||
|
'SALES' AS transaction_type,
|
||||||
|
mdp.id::BIGINT AS transaction_id,
|
||||||
|
c.id::BIGINT AS customer_id,
|
||||||
|
m.so_date::DATE AS trans_date,
|
||||||
|
mdp.delivery_date::DATE AS delivery_date,
|
||||||
|
m.so_number || '-' || TO_CHAR(mdp.delivery_date, 'YYYYMMDD') || '-' || CAST(pw.warehouse_id AS VARCHAR) AS reference,
|
||||||
|
COALESCE(mdp.vehicle_number, '') AS vehicle_numbers,
|
||||||
|
|
||||||
|
COALESCE(mdp.usage_qty, 0)::NUMERIC(15,3) AS qty,
|
||||||
|
COALESCE(mdp.total_weight, 0)::NUMERIC(15,3) AS weight,
|
||||||
|
COALESCE(mdp.avg_weight, 0)::NUMERIC(15,3) AS average_weight,
|
||||||
|
COALESCE(mdp.unit_price, 0)::NUMERIC(15,3) AS price,
|
||||||
|
COALESCE(mdp.total_price, 0)::NUMERIC(15,3) AS final_price,
|
||||||
|
COALESCE(mdp.total_price, 0)::NUMERIC(15,3) AS total_price,
|
||||||
|
0::NUMERIC(15,3) AS payment_amount,
|
||||||
|
w.name AS pickup_info,
|
||||||
|
u.name AS sales_person
|
||||||
|
`).
|
||||||
|
Joins("INNER JOIN marketing_products mp ON mp.id = mdp.marketing_product_id").
|
||||||
|
Joins("INNER JOIN marketings m ON m.id = mp.marketing_id").
|
||||||
|
Joins("INNER JOIN customers c ON c.id = m.customer_id").
|
||||||
|
Joins("INNER JOIN product_warehouses pw ON pw.id = mdp.product_warehouse_id").
|
||||||
|
Joins("INNER JOIN warehouses w ON w.id = pw.warehouse_id").
|
||||||
|
Joins("INNER JOIN users u ON u.id = m.sales_person_id").
|
||||||
|
Where("mdp.delivery_date IS NOT NULL").
|
||||||
|
Where("m.deleted_at IS NULL").
|
||||||
|
Where("c.deleted_at IS NULL")
|
||||||
|
|
||||||
|
if customerID != nil {
|
||||||
|
salesQuery = salesQuery.Where("c.id = ?", *customerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentQuery := r.db.WithContext(ctx).
|
||||||
|
Table("payments p").
|
||||||
|
Select(`
|
||||||
|
'PAYMENT' AS transaction_type,
|
||||||
|
p.id::BIGINT AS transaction_id,
|
||||||
|
c.id::BIGINT AS customer_id,
|
||||||
|
p.payment_date::DATE AS trans_date,
|
||||||
|
NULL AS delivery_date,
|
||||||
|
COALESCE(p.reference_number, p.payment_code) AS reference,
|
||||||
|
'-' AS vehicle_numbers,
|
||||||
|
0::NUMERIC(15,3) AS qty,
|
||||||
|
0::NUMERIC(15,3) AS weight,
|
||||||
|
0::NUMERIC(15,3) AS average_weight,
|
||||||
|
0::NUMERIC(15,3) AS price,
|
||||||
|
0::NUMERIC(15,3) AS final_price,
|
||||||
|
0::NUMERIC(15,3) AS total_price,
|
||||||
|
p.nominal::NUMERIC(15,3) AS payment_amount,
|
||||||
|
'-' AS pickup_info,
|
||||||
|
'-' AS sales_person
|
||||||
|
`).
|
||||||
|
Joins("INNER JOIN customers c ON c.id = p.party_id").
|
||||||
|
Where("p.party_type = ?", "CUSTOMER").
|
||||||
|
Where("p.direction = ?", "IN").
|
||||||
|
Where("p.transaction_type = ?", "PENJUALAN").
|
||||||
|
Where("p.deleted_at IS NULL").
|
||||||
|
Where("c.deleted_at IS NULL")
|
||||||
|
|
||||||
|
if customerID != nil {
|
||||||
|
paymentQuery = paymentQuery.Where("c.id = ?", *customerID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []CustomerPaymentTransaction
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Raw("? UNION ALL ? ORDER BY customer_id, trans_date, transaction_type DESC, transaction_id",
|
||||||
|
salesQuery,
|
||||||
|
paymentQuery,
|
||||||
|
).
|
||||||
|
Scan(&results).
|
||||||
|
Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *customerPaymentRepositoryImpl) GetInitialBalanceByCustomer(ctx context.Context, customerID uint) (float64, error) {
|
||||||
|
var result struct {
|
||||||
|
Nominal float64
|
||||||
|
}
|
||||||
|
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("payments").
|
||||||
|
Select("COALESCE(SUM(nominal), 0) as nominal").
|
||||||
|
Where("party_type = ?", "CUSTOMER").
|
||||||
|
Where("party_id = ?", customerID).
|
||||||
|
Where("transaction_type = ?", "SALDO_AWAL").
|
||||||
|
Where("deleted_at IS NULL").
|
||||||
|
Scan(&result).
|
||||||
|
Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.Nominal, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *customerPaymentRepositoryImpl) GetCustomerIDsWithTransactions(ctx context.Context, limit, offset int) ([]uint, int64, error) {
|
||||||
|
subQuery := r.db.WithContext(ctx).
|
||||||
|
Table("(" +
|
||||||
|
"SELECT DISTINCT c.id as customer_id FROM marketing_delivery_products mdp " +
|
||||||
|
"INNER JOIN marketing_products mp ON mp.id = mdp.marketing_product_id " +
|
||||||
|
"INNER JOIN marketings m ON m.id = mp.marketing_id " +
|
||||||
|
"INNER JOIN customers c ON c.id = m.customer_id " +
|
||||||
|
"WHERE mdp.delivery_date IS NOT NULL AND m.deleted_at IS NULL AND c.deleted_at IS NULL " +
|
||||||
|
"UNION " +
|
||||||
|
"SELECT DISTINCT c.id as customer_id FROM payments p " +
|
||||||
|
"INNER JOIN customers c ON c.id = p.party_id " +
|
||||||
|
"WHERE p.party_type = 'CUSTOMER' AND p.direction = 'IN' " +
|
||||||
|
"AND p.transaction_type = 'PENJUALAN' AND p.deleted_at IS NULL AND c.deleted_at IS NULL" +
|
||||||
|
") as customer_ids")
|
||||||
|
|
||||||
|
var total int64
|
||||||
|
if err := subQuery.Count(&total).Error; err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var customerIDs []uint
|
||||||
|
err := r.db.WithContext(ctx).
|
||||||
|
Table("("+
|
||||||
|
"SELECT DISTINCT c.id as customer_id FROM marketing_delivery_products mdp "+
|
||||||
|
"INNER JOIN marketing_products mp ON mp.id = mdp.marketing_product_id "+
|
||||||
|
"INNER JOIN marketings m ON m.id = mp.marketing_id "+
|
||||||
|
"INNER JOIN customers c ON c.id = m.customer_id "+
|
||||||
|
"WHERE mdp.delivery_date IS NOT NULL AND m.deleted_at IS NULL AND c.deleted_at IS NULL "+
|
||||||
|
"UNION "+
|
||||||
|
"SELECT DISTINCT c.id as customer_id FROM payments p "+
|
||||||
|
"INNER JOIN customers c ON c.id = p.party_id "+
|
||||||
|
"WHERE p.party_type = 'CUSTOMER' AND p.direction = 'IN' "+
|
||||||
|
"AND p.transaction_type = 'PENJUALAN' AND p.deleted_at IS NULL AND c.deleted_at IS NULL"+
|
||||||
|
") as customer_ids").
|
||||||
|
Select("customer_id").
|
||||||
|
Order("customer_id ASC").
|
||||||
|
Limit(limit).
|
||||||
|
Offset(offset).
|
||||||
|
Pluck("customer_id", &customerIDs).
|
||||||
|
Error
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return customerIDs, total, nil
|
||||||
|
}
|
||||||
@@ -11,6 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type HppPerKandangRow struct {
|
type HppPerKandangRow struct {
|
||||||
|
ProjectFlockKandangID uint
|
||||||
KandangID uint
|
KandangID uint
|
||||||
KandangName string
|
KandangName string
|
||||||
KandangStatus string
|
KandangStatus string
|
||||||
@@ -18,6 +19,7 @@ type HppPerKandangRow struct {
|
|||||||
LocationName string
|
LocationName string
|
||||||
PicID uint
|
PicID uint
|
||||||
PicName string
|
PicName string
|
||||||
|
RecordingCount int64
|
||||||
RemainingChickenBirds float64
|
RemainingChickenBirds float64
|
||||||
RemainingChickenWeight float64
|
RemainingChickenWeight float64
|
||||||
EggProductionWeightKg float64
|
EggProductionWeightKg float64
|
||||||
@@ -44,7 +46,8 @@ type HppPerKandangSupplierRow struct {
|
|||||||
|
|
||||||
type HppPerKandangRepository interface {
|
type HppPerKandangRepository interface {
|
||||||
GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error)
|
GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error)
|
||||||
GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error)
|
GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error)
|
||||||
|
GetEggProductionByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type hppPerKandangRepository struct {
|
type hppPerKandangRepository struct {
|
||||||
@@ -58,9 +61,31 @@ func NewHppPerKandangRepository(db *gorm.DB) HppPerKandangRepository {
|
|||||||
func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error) {
|
func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangRow, error) {
|
||||||
var rows []HppPerKandangRow
|
var rows []HppPerKandangRow
|
||||||
|
|
||||||
query := r.db.WithContext(ctx).
|
latestApproval := r.db.WithContext(ctx).
|
||||||
|
Table("approvals AS a").
|
||||||
|
Select("a.approvable_id, a.action").
|
||||||
|
Joins(`
|
||||||
|
JOIN (
|
||||||
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
||||||
|
FROM approvals
|
||||||
|
WHERE approvable_type = ?
|
||||||
|
GROUP BY approvable_id
|
||||||
|
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
||||||
|
string(utils.ApprovalWorkflowRecording),
|
||||||
|
)
|
||||||
|
|
||||||
|
validRecordings := r.db.WithContext(ctx).
|
||||||
Table("recordings AS r").
|
Table("recordings AS r").
|
||||||
|
Select("r.id, r.project_flock_kandangs_id, r.total_chick_qty").
|
||||||
|
Joins("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
||||||
|
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||||
|
Where("r.deleted_at IS NULL").
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected))
|
||||||
|
|
||||||
|
query := r.db.WithContext(ctx).
|
||||||
|
Table("project_flocks AS pf").
|
||||||
Select(`
|
Select(`
|
||||||
|
pfk.id AS project_flock_kandang_id,
|
||||||
k.id AS kandang_id,
|
k.id AS kandang_id,
|
||||||
k.name AS kandang_name,
|
k.name AS kandang_name,
|
||||||
k.status AS kandang_status,
|
k.status AS kandang_status,
|
||||||
@@ -68,22 +93,21 @@ func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, en
|
|||||||
loc.name AS location_name,
|
loc.name AS location_name,
|
||||||
pic.id AS pic_id,
|
pic.id AS pic_id,
|
||||||
pic.name AS pic_name,
|
pic.name AS pic_name,
|
||||||
COALESCE(MAX(r.total_chick_qty), 0) AS remaining_chicken_birds,
|
COALESCE(COUNT(vr.id), 0) AS recording_count,
|
||||||
COALESCE(SUM(rbw.total_weight), 0) AS remaining_chicken_weight,
|
COALESCE(MAX(vr.total_chick_qty), 0) AS remaining_chicken_birds,
|
||||||
COALESCE(SUM(re.weight), 0) AS egg_production_weight_kg,
|
0 AS remaining_chicken_weight,
|
||||||
COALESCE(SUM(re.qty), 0) AS egg_production_pieces`).
|
0 AS egg_production_weight_kg,
|
||||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
0 AS egg_production_pieces`).
|
||||||
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.project_flock_id = pf.id").
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN users AS pic ON pic.id = k.pic_id").
|
Joins("JOIN users AS pic ON pic.id = k.pic_id").
|
||||||
Joins("LEFT JOIN recording_bws AS rbw ON rbw.recording_id = r.id").
|
Joins("LEFT JOIN (?) AS vr ON vr.project_flock_kandangs_id = pfk.id", validRecordings).
|
||||||
Joins("LEFT JOIN recording_eggs AS re ON re.recording_id = r.id").
|
Where("pfk.closed_at IS NULL")
|
||||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
||||||
Where("r.deleted_at IS NULL")
|
|
||||||
|
|
||||||
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
|
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
|
||||||
|
|
||||||
query = query.Group("k.id, k.name, k.status, loc.id, loc.name, pic.id, pic.name").
|
query = query.Group("pfk.id, k.id, k.name, k.status, loc.id, loc.name, pic.id, pic.name").
|
||||||
Order("k.id ASC")
|
Order("k.id ASC")
|
||||||
|
|
||||||
if err := query.Scan(&rows).Error; err != nil {
|
if err := query.Scan(&rows).Error; err != nil {
|
||||||
@@ -93,41 +117,44 @@ func (r *hppPerKandangRepository) GetRowsByPeriod(ctx context.Context, start, en
|
|||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, areaIDs, locationIDs, kandangIDs []int64) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error) {
|
func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) ([]HppPerKandangCostRow, []HppPerKandangSupplierRow, error) {
|
||||||
var rows []HppPerKandangCostRow
|
var rows []HppPerKandangCostRow
|
||||||
|
|
||||||
recordingPfk := r.db.WithContext(ctx).
|
|
||||||
Table("recordings AS r").
|
|
||||||
Select("DISTINCT pfk.id").
|
|
||||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
|
||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
|
||||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
|
||||||
Where("r.deleted_at IS NULL")
|
|
||||||
recordingPfk = applyLocationFilters(recordingPfk, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
purchaseStockableKey := fifo.StockableKeyPurchaseItems.String()
|
purchaseStockableKey := fifo.StockableKeyPurchaseItems.String()
|
||||||
transferStockableKey := fifo.StockableKeyStockTransferIn.String()
|
transferStockableKey := fifo.StockableKeyStockTransferIn.String()
|
||||||
|
|
||||||
|
latestApproval := r.db.WithContext(ctx).
|
||||||
|
Table("approvals AS a").
|
||||||
|
Select("a.approvable_id, a.action").
|
||||||
|
Joins(`
|
||||||
|
JOIN (
|
||||||
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
||||||
|
FROM approvals
|
||||||
|
WHERE approvable_type = ?
|
||||||
|
GROUP BY approvable_id
|
||||||
|
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
||||||
|
string(utils.ApprovalWorkflowRecording),
|
||||||
|
)
|
||||||
|
|
||||||
query := r.db.WithContext(ctx).
|
query := r.db.WithContext(ctx).
|
||||||
Table("recordings AS r").
|
Table("recordings AS r").
|
||||||
Select(`
|
Select(`
|
||||||
k.id AS kandang_id,
|
k.id AS kandang_id,
|
||||||
COALESCE(SUM(CASE
|
COALESCE(SUM(CASE
|
||||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END), 0) AS feed_cost,
|
END), 0) AS feed_cost,
|
||||||
COALESCE(SUM(CASE
|
COALESCE(SUM(CASE
|
||||||
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
WHEN f.name = ? THEN COALESCE(sa.qty, 0) * COALESCE(pi.price, 0)
|
||||||
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.quantity, 0) * COALESCE(tpi.price, 0)
|
WHEN sa.stockable_type = ? AND tf.name = ? THEN COALESCE(std.total_qty, 0) * COALESCE(tpi.price, 0)
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END), 0) AS ovk_cost`,
|
END), 0) AS ovk_cost`,
|
||||||
utils.FlagPakan, transferStockableKey, utils.FlagPakan,
|
utils.FlagPakan, transferStockableKey, utils.FlagPakan,
|
||||||
utils.FlagOVK, transferStockableKey, utils.FlagOVK).
|
utils.FlagOVK, transferStockableKey, utils.FlagOVK).
|
||||||
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
Joins("JOIN project_flock_kandangs AS pfk ON pfk.id = r.project_flock_kandangs_id").
|
||||||
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
Joins("JOIN kandangs AS k ON k.id = pfk.kandang_id").
|
||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
Joins("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
||||||
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
Joins("LEFT JOIN recording_stocks AS rs ON rs.recording_id = r.id").
|
||||||
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
Joins("LEFT JOIN stock_allocations AS sa ON sa.usable_type = ? AND sa.usable_id = rs.id AND sa.status = ?", fifo.UsableKeyRecordingStock.String(), entity.StockAllocationStatusActive).
|
||||||
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
Joins("LEFT JOIN purchase_items AS pi ON pi.id = sa.stockable_id AND sa.stockable_type = ?", purchaseStockableKey).
|
||||||
@@ -136,11 +163,10 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
Joins("LEFT JOIN purchase_items AS tpi ON tpi.product_id = std.product_id AND tpi.warehouse_id = st.from_warehouse_id").
|
Joins("LEFT JOIN purchase_items AS tpi ON tpi.product_id = std.product_id AND tpi.warehouse_id = st.from_warehouse_id").
|
||||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
Joins("LEFT JOIN flags AS tf ON tf.flagable_id = std.product_id AND tf.flagable_type = ?", entity.FlagableTypeProduct).
|
Joins("LEFT JOIN flags AS tf ON tf.flagable_id = std.product_id AND tf.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
Where("r.project_flock_kandangs_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
Where("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
||||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
// Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||||
Where("r.deleted_at IS NULL")
|
Where("r.deleted_at IS NULL").
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected))
|
||||||
query = applyLocationFilters(query, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
query = query.Group("k.id").Order("k.id ASC")
|
query = query.Group("k.id").Order("k.id ASC")
|
||||||
|
|
||||||
@@ -172,9 +198,8 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
Joins("LEFT JOIN purchase_items AS pi ON pi.product_warehouse_id = pc.product_warehouse_id").
|
Joins("LEFT JOIN purchase_items AS pi ON pi.product_warehouse_id = pc.product_warehouse_id").
|
||||||
Joins("LEFT JOIN purchases AS pur ON pur.id = pi.purchase_id").
|
Joins("LEFT JOIN purchases AS pur ON pur.id = pi.purchase_id").
|
||||||
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
|
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
|
||||||
Where("pc.project_flock_kandang_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
Where("pc.project_flock_kandang_id IN ?", projectFlockKandangIDs).
|
||||||
Group("pfk.kandang_id, s.id, s.name, s.alias")
|
Group("pfk.kandang_id, s.id, s.name, s.alias")
|
||||||
docQuery = applyLocationFilters(docQuery, areaIDs, locationIDs, kandangIDs)
|
|
||||||
|
|
||||||
if err := docQuery.Scan(&docRows).Error; err != nil {
|
if err := docQuery.Scan(&docRows).Error; err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -254,9 +279,9 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
Joins("JOIN project_budgets AS pb ON pb.project_flock_id = pfk.project_flock_id").
|
Joins("JOIN project_budgets AS pb ON pb.project_flock_id = pfk.project_flock_id").
|
||||||
Joins("LEFT JOIN (?) AS k_usage ON k_usage.project_flock_kandang_id = pfk.id", pfkUsageSub).
|
Joins("LEFT JOIN (?) AS k_usage ON k_usage.project_flock_kandang_id = pfk.id", pfkUsageSub).
|
||||||
Joins("LEFT JOIN (?) AS p_usage ON p_usage.project_flock_id = pfk.project_flock_id", projectUsageSub).
|
Joins("LEFT JOIN (?) AS p_usage ON p_usage.project_flock_id = pfk.project_flock_id", projectUsageSub).
|
||||||
Where("pfk.id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
Where("pfk.id IN (?)", projectFlockKandangIDs).
|
||||||
Group("k.id")
|
Group("k.id")
|
||||||
budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs)
|
// budgetQuery = applyLocationFilters(budgetQuery, areaIDs, locationIDs, kandangIDs)
|
||||||
|
|
||||||
if err := budgetQuery.Scan(&budgetRows).Error; err != nil {
|
if err := budgetQuery.Scan(&budgetRows).Error; err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -288,9 +313,9 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
Joins("JOIN locations AS loc ON loc.id = k.location_id").
|
||||||
Joins("JOIN expense_nonstocks AS en ON en.project_flock_kandang_id = pfk.id").
|
Joins("JOIN expense_nonstocks AS en ON en.project_flock_kandang_id = pfk.id").
|
||||||
Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id").
|
Joins("JOIN expense_realizations AS er ON er.expense_nonstock_id = en.id").
|
||||||
Where("pfk.id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
Where("pfk.id IN (?)", projectFlockKandangIDs).
|
||||||
Group("k.id")
|
Group("k.id")
|
||||||
expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs)
|
// expenseQuery = applyLocationFilters(expenseQuery, areaIDs, locationIDs, kandangIDs)
|
||||||
|
|
||||||
if err := expenseQuery.Scan(&expenseRows).Error; err != nil {
|
if err := expenseQuery.Scan(&expenseRows).Error; err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -323,10 +348,10 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
|
Joins("LEFT JOIN suppliers AS s ON s.id = pur.supplier_id").
|
||||||
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
Joins("LEFT JOIN flags AS f ON f.flagable_id = pi.product_id AND f.flagable_type = ?", entity.FlagableTypeProduct).
|
||||||
Where("f.name IN ?", []utils.FlagType{utils.FlagPakan, utils.FlagOVK}).
|
Where("f.name IN ?", []utils.FlagType{utils.FlagPakan, utils.FlagOVK}).
|
||||||
Where("r.project_flock_kandangs_id IN (?)", recordingPfk.Session(&gorm.Session{NewDB: true})).
|
Where("r.project_flock_kandangs_id IN (?)", projectFlockKandangIDs).
|
||||||
Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
// Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||||
Where("r.deleted_at IS NULL")
|
Where("r.deleted_at IS NULL")
|
||||||
feedQuery = applyLocationFilters(feedQuery, areaIDs, locationIDs, kandangIDs)
|
// feedQuery = applyLocationFilters(feedQuery, areaIDs, locationIDs, kandangIDs)
|
||||||
|
|
||||||
if err := feedQuery.Scan(&feedSuppliers).Error; err != nil {
|
if err := feedQuery.Scan(&feedSuppliers).Error; err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
@@ -347,6 +372,61 @@ func (r *hppPerKandangRepository) GetFeedOvkDocCostByPeriod(ctx context.Context,
|
|||||||
return rows, supplierRows, nil
|
return rows, supplierRows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *hppPerKandangRepository) GetEggProductionByProjectFlockKandangIDs(ctx context.Context, start, end time.Time, projectFlockKandangIDs []uint) (map[uint]HppPerKandangRow, error) {
|
||||||
|
if len(projectFlockKandangIDs) == 0 {
|
||||||
|
return map[uint]HppPerKandangRow{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
latestApproval := r.db.WithContext(ctx).
|
||||||
|
Table("approvals AS a").
|
||||||
|
Select("a.approvable_id, a.action").
|
||||||
|
Joins(`
|
||||||
|
JOIN (
|
||||||
|
SELECT approvable_id, MAX(action_at) AS latest_action_at
|
||||||
|
FROM approvals
|
||||||
|
WHERE approvable_type = ?
|
||||||
|
GROUP BY approvable_id
|
||||||
|
) AS la ON la.approvable_id = a.approvable_id AND la.latest_action_at = a.action_at`,
|
||||||
|
string(utils.ApprovalWorkflowRecording),
|
||||||
|
)
|
||||||
|
|
||||||
|
type eggRow struct {
|
||||||
|
ProjectFlockKandangID uint
|
||||||
|
EggProductionWeightKg float64
|
||||||
|
EggProductionPieces float64
|
||||||
|
}
|
||||||
|
|
||||||
|
eggRows := make([]eggRow, 0)
|
||||||
|
query := r.db.WithContext(ctx).
|
||||||
|
Table("recordings AS r").
|
||||||
|
Select(`
|
||||||
|
r.project_flock_kandangs_id AS project_flock_kandang_id,
|
||||||
|
COALESCE(SUM(re.weight), 0) AS egg_production_weight_kg,
|
||||||
|
COALESCE(SUM(re.qty), 0) AS egg_production_pieces`).
|
||||||
|
Joins("LEFT JOIN (?) AS la ON la.approvable_id = r.id", latestApproval).
|
||||||
|
Joins("LEFT JOIN recording_eggs AS re ON re.recording_id = r.id").
|
||||||
|
Where("r.project_flock_kandangs_id IN ?", projectFlockKandangIDs).
|
||||||
|
// Where("r.record_datetime >= ? AND r.record_datetime < ?", start, end).
|
||||||
|
Where("r.deleted_at IS NULL").
|
||||||
|
Where("(la.action IS NULL OR la.action != ?)", string(entity.ApprovalActionRejected)).
|
||||||
|
Group("r.project_flock_kandangs_id")
|
||||||
|
|
||||||
|
if err := query.Scan(&eggRows).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result := make(map[uint]HppPerKandangRow, len(eggRows))
|
||||||
|
for _, row := range eggRows {
|
||||||
|
result[row.ProjectFlockKandangID] = HppPerKandangRow{
|
||||||
|
ProjectFlockKandangID: row.ProjectFlockKandangID,
|
||||||
|
EggProductionWeightKg: row.EggProductionWeightKg,
|
||||||
|
EggProductionPieces: row.EggProductionPieces,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func applyLocationFilters(query *gorm.DB, areaIDs, locationIDs, kandangIDs []int64) *gorm.DB {
|
func applyLocationFilters(query *gorm.DB, areaIDs, locationIDs, kandangIDs []int64) *gorm.DB {
|
||||||
if len(areaIDs) > 0 {
|
if len(areaIDs) > 0 {
|
||||||
query = query.Where("loc.area_id IN ?", areaIDs)
|
query = query.Where("loc.area_id IN ?", areaIDs)
|
||||||
|
|||||||
@@ -21,5 +21,5 @@ func RepportRoutes(v1 fiber.Router, u user.UserService, s repport.RepportService
|
|||||||
route.Get("/debt-supplier", m.RequirePermissions(m.P_ReportDebtSupplierGetAll), ctrl.GetDebtSupplier)
|
route.Get("/debt-supplier", m.RequirePermissions(m.P_ReportDebtSupplierGetAll), ctrl.GetDebtSupplier)
|
||||||
route.Get("/hpp-per-kandang", m.RequirePermissions(m.P_ReportHppPerKandangGetAll), ctrl.GetHppPerKandang)
|
route.Get("/hpp-per-kandang", m.RequirePermissions(m.P_ReportHppPerKandangGetAll), ctrl.GetHppPerKandang)
|
||||||
route.Get("/production-result/:idProjectFlockKandang", m.RequirePermissions(m.P_ReportProductionResultGetAll), ctrl.GetProductionResult)
|
route.Get("/production-result/:idProjectFlockKandang", m.RequirePermissions(m.P_ReportProductionResultGetAll), ctrl.GetProductionResult)
|
||||||
|
route.Get("/customer-payment", m.RequirePermissions(m.P_ReportCustomerPaymentGetAll), ctrl.GetCustomerPayment)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
expenseRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/expenses/repositories"
|
||||||
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
marketingRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/marketing/repositories"
|
||||||
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
areaDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/areas/dto"
|
||||||
|
customerRepo "gitlab.com/mbugroup/lti-api.git/internal/modules/master/customers/repositories"
|
||||||
supplierDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/suppliers/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"
|
warehouseDTO "gitlab.com/mbugroup/lti-api.git/internal/modules/master/warehouses/dto"
|
||||||
productionStandardRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
productionStandardRepository "gitlab.com/mbugroup/lti-api.git/internal/modules/master/production-standards/repositories"
|
||||||
@@ -42,11 +43,13 @@ type RepportService interface {
|
|||||||
GetDebtSupplier(ctx *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error)
|
GetDebtSupplier(ctx *fiber.Ctx, params *validation.DebtSupplierQuery) ([]dto.DebtSupplierDTO, int64, error)
|
||||||
GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error)
|
GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangResponseData, *dto.HppPerKandangMetaDTO, error)
|
||||||
GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error)
|
GetProductionResult(ctx *fiber.Ctx, params *validation.ProductionResultQuery) ([]dto.ProductionResultDTO, int64, error)
|
||||||
|
GetCustomerPayment(ctx *fiber.Ctx, params *validation.CustomerPaymentQuery) ([]dto.CustomerPaymentReportItem, int64, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type repportService struct {
|
type repportService struct {
|
||||||
Log *logrus.Logger
|
Log *logrus.Logger
|
||||||
Validate *validator.Validate
|
Validate *validator.Validate
|
||||||
|
DB *gorm.DB
|
||||||
ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository
|
ExpenseRealizationRepo expenseRepo.ExpenseRealizationRepository
|
||||||
MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository
|
MarketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository
|
||||||
PurchaseRepo purchaseRepo.PurchaseRepository
|
PurchaseRepo purchaseRepo.PurchaseRepository
|
||||||
@@ -57,6 +60,8 @@ type repportService struct {
|
|||||||
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
DebtSupplierRepo repportRepo.DebtSupplierRepository
|
||||||
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
HppPerKandangRepo repportRepo.HppPerKandangRepository
|
||||||
ProductionResultRepo repportRepo.ProductionResultRepository
|
ProductionResultRepo repportRepo.ProductionResultRepository
|
||||||
|
CustomerPaymentRepo repportRepo.CustomerPaymentRepository
|
||||||
|
CustomerRepo customerRepo.CustomerRepository
|
||||||
StandardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository
|
StandardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository
|
||||||
ProductionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository
|
ProductionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository
|
||||||
}
|
}
|
||||||
@@ -71,6 +76,7 @@ type HppCostAggregate struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func NewRepportService(
|
func NewRepportService(
|
||||||
|
db *gorm.DB,
|
||||||
validate *validator.Validate,
|
validate *validator.Validate,
|
||||||
expenseRealizationRepo expenseRepo.ExpenseRealizationRepository,
|
expenseRealizationRepo expenseRepo.ExpenseRealizationRepository,
|
||||||
marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository,
|
marketingDeliveryRepo marketingRepo.MarketingDeliveryProductRepository,
|
||||||
@@ -82,12 +88,15 @@ func NewRepportService(
|
|||||||
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
debtSupplierRepo repportRepo.DebtSupplierRepository,
|
||||||
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
hppPerKandangRepo repportRepo.HppPerKandangRepository,
|
||||||
productionResultRepo repportRepo.ProductionResultRepository,
|
productionResultRepo repportRepo.ProductionResultRepository,
|
||||||
|
customerPaymentRepo repportRepo.CustomerPaymentRepository,
|
||||||
|
customerRepo customerRepo.CustomerRepository,
|
||||||
standardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository,
|
standardGrowthDetailRepo productionStandardRepository.StandardGrowthDetailRepository,
|
||||||
productionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository,
|
productionStandardDetailRepo productionStandardRepository.ProductionStandardDetailRepository,
|
||||||
) RepportService {
|
) RepportService {
|
||||||
return &repportService{
|
return &repportService{
|
||||||
Log: utils.Log,
|
Log: utils.Log,
|
||||||
Validate: validate,
|
Validate: validate,
|
||||||
|
DB: db,
|
||||||
ExpenseRealizationRepo: expenseRealizationRepo,
|
ExpenseRealizationRepo: expenseRealizationRepo,
|
||||||
MarketingDeliveryRepo: marketingDeliveryRepo,
|
MarketingDeliveryRepo: marketingDeliveryRepo,
|
||||||
PurchaseRepo: purchaseRepo,
|
PurchaseRepo: purchaseRepo,
|
||||||
@@ -98,6 +107,8 @@ func NewRepportService(
|
|||||||
DebtSupplierRepo: debtSupplierRepo,
|
DebtSupplierRepo: debtSupplierRepo,
|
||||||
HppPerKandangRepo: hppPerKandangRepo,
|
HppPerKandangRepo: hppPerKandangRepo,
|
||||||
ProductionResultRepo: productionResultRepo,
|
ProductionResultRepo: productionResultRepo,
|
||||||
|
CustomerPaymentRepo: customerPaymentRepo,
|
||||||
|
CustomerRepo: customerRepo,
|
||||||
StandardGrowthDetailRepo: standardGrowthDetailRepo,
|
StandardGrowthDetailRepo: standardGrowthDetailRepo,
|
||||||
ProductionStandardDetailRepo: productionStandardDetailRepo,
|
ProductionStandardDetailRepo: productionStandardDetailRepo,
|
||||||
}
|
}
|
||||||
@@ -390,6 +401,205 @@ func (s *repportService) GetProductionResult(ctx *fiber.Ctx, params *validation.
|
|||||||
return weeklyResults, totalWeeks, nil
|
return weeklyResults, totalWeeks, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *repportService) GetCustomerPayment(ctx *fiber.Ctx, params *validation.CustomerPaymentQuery) ([]dto.CustomerPaymentReportItem, int64, error) {
|
||||||
|
if err := s.Validate.Struct(params); err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine customer IDs to process
|
||||||
|
var customerIDs []uint
|
||||||
|
var totalCustomers int64
|
||||||
|
|
||||||
|
if len(params.CustomerIDs) > 0 {
|
||||||
|
// Specific customer IDs mode (no pagination)
|
||||||
|
customerIDs = params.CustomerIDs
|
||||||
|
totalCustomers = int64(len(customerIDs))
|
||||||
|
|
||||||
|
if len(customerIDs) == 0 {
|
||||||
|
return []dto.CustomerPaymentReportItem{}, 0, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Multiple customers mode with pagination
|
||||||
|
page := params.Page
|
||||||
|
limit := params.Limit
|
||||||
|
if page < 1 {
|
||||||
|
page = 1
|
||||||
|
}
|
||||||
|
if limit < 1 {
|
||||||
|
limit = 10
|
||||||
|
}
|
||||||
|
|
||||||
|
offset := (page - 1) * limit
|
||||||
|
|
||||||
|
var err error
|
||||||
|
customerIDs, totalCustomers, err = s.CustomerPaymentRepo.GetCustomerIDsWithTransactions(ctx.Context(), limit, offset)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(customerIDs) == 0 {
|
||||||
|
return []dto.CustomerPaymentReportItem{}, 0, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []dto.CustomerPaymentReportItem
|
||||||
|
for _, customerID := range customerIDs {
|
||||||
|
item, err := s.processCustomerPayment(ctx.Context(), customerID, params)
|
||||||
|
if err != nil {
|
||||||
|
return nil, 0, err
|
||||||
|
}
|
||||||
|
result = append(result, item)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, totalCustomers, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *repportService) processCustomerPayment(ctx context.Context, customerID uint, params *validation.CustomerPaymentQuery) (dto.CustomerPaymentReportItem, error) {
|
||||||
|
|
||||||
|
customer, err := s.CustomerRepo.GetByID(ctx, customerID, nil)
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
initialBalance, err := s.CustomerPaymentRepo.GetInitialBalanceByCustomer(ctx, customerID)
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cid := customerID
|
||||||
|
transactions, err := s.CustomerPaymentRepo.GetCustomerPaymentTransactions(ctx, &cid)
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows := make([]dto.CustomerPaymentReportRow, 0, len(transactions))
|
||||||
|
runningBalance := initialBalance
|
||||||
|
|
||||||
|
for i, tx := range transactions {
|
||||||
|
|
||||||
|
previousBalance := runningBalance
|
||||||
|
|
||||||
|
row := dto.ToCustomerPaymentReportRow(tx)
|
||||||
|
|
||||||
|
if tx.TransactionType == "SALES" {
|
||||||
|
runningBalance -= tx.TotalPrice
|
||||||
|
status, paymentDate := s.determineSalesStatusAndPaymentDate(transactions, i, previousBalance, runningBalance)
|
||||||
|
row.Status = status
|
||||||
|
|
||||||
|
if status == "LUNAS" {
|
||||||
|
if previousBalance >= tx.TotalPrice {
|
||||||
|
days := 0
|
||||||
|
row.AgingDay = &days
|
||||||
|
} else if paymentDate != nil {
|
||||||
|
days := int(paymentDate.Sub(tx.TransDate).Hours() / 24)
|
||||||
|
if days < 0 {
|
||||||
|
days = 0
|
||||||
|
}
|
||||||
|
row.AgingDay = &days
|
||||||
|
} else {
|
||||||
|
days := 0
|
||||||
|
row.AgingDay = &days
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
days := int(time.Since(tx.TransDate).Hours() / 24)
|
||||||
|
if days < 0 {
|
||||||
|
days = 0
|
||||||
|
}
|
||||||
|
row.AgingDay = &days
|
||||||
|
}
|
||||||
|
} else if tx.TransactionType == "PAYMENT" {
|
||||||
|
runningBalance += tx.PaymentAmount
|
||||||
|
row.Status = ""
|
||||||
|
row.AgingDay = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
row.AccountsReceivable = runningBalance
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
if params.StartDate != "" || params.EndDate != "" {
|
||||||
|
filteredRows := make([]dto.CustomerPaymentReportRow, 0, len(rows))
|
||||||
|
location, err := time.LoadLocation("Asia/Jakarta")
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var startDate, endDate *time.Time
|
||||||
|
if params.StartDate != "" {
|
||||||
|
parsed, err := time.ParseInLocation("2006-01-02", params.StartDate, location)
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
startDate = &parsed
|
||||||
|
}
|
||||||
|
if params.EndDate != "" {
|
||||||
|
parsed, err := time.ParseInLocation("2006-01-02", params.EndDate, location)
|
||||||
|
if err != nil {
|
||||||
|
return dto.CustomerPaymentReportItem{}, err
|
||||||
|
}
|
||||||
|
endOfDay := time.Date(parsed.Year(), parsed.Month(), parsed.Day(), 23, 59, 59, 999999999, location)
|
||||||
|
endDate = &endOfDay
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, row := range rows {
|
||||||
|
transDate := row.TransDate.In(location)
|
||||||
|
if startDate != nil && transDate.Before(*startDate) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if endDate != nil && transDate.After(*endDate) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
filteredRows = append(filteredRows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
rows = filteredRows
|
||||||
|
}
|
||||||
|
|
||||||
|
summary := dto.ToCustomerPaymentReportSummary(rows, initialBalance)
|
||||||
|
|
||||||
|
return dto.ToCustomerPaymentReportItem(*customer, initialBalance, rows, summary), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *repportService) determineSalesStatusAndPaymentDate(transactions []repportRepo.CustomerPaymentTransaction, currentIndex int, previousBalance, currentBalance float64) (string, *time.Time) {
|
||||||
|
currentSales := transactions[currentIndex]
|
||||||
|
|
||||||
|
// Status Logic:
|
||||||
|
// 1. LUNAS: previousBalance >= salesAmount (paid from deposit)
|
||||||
|
// 2. LUNAS: future payments make AR >= 0 (eventually paid)
|
||||||
|
// 3. DIBAYAR SEBAGIAN: has payment but not enough
|
||||||
|
// 4. BELUM LUNAS: no payment at all
|
||||||
|
|
||||||
|
if previousBalance >= currentSales.TotalPrice {
|
||||||
|
return "LUNAS", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
hasPartialPaymentFromBalance := previousBalance > 0 && previousBalance < currentSales.TotalPrice
|
||||||
|
|
||||||
|
futureBalance := currentBalance
|
||||||
|
hasPayment := false
|
||||||
|
var paymentDateThatMadeItLunas *time.Time
|
||||||
|
|
||||||
|
for i := currentIndex + 1; i < len(transactions); i++ {
|
||||||
|
if transactions[i].TransactionType == "PAYMENT" {
|
||||||
|
futureBalance += transactions[i].PaymentAmount
|
||||||
|
hasPayment = true
|
||||||
|
|
||||||
|
if futureBalance >= 0 {
|
||||||
|
paymentDateThatMadeItLunas = &transactions[i].TransDate
|
||||||
|
return "LUNAS", paymentDateThatMadeItLunas
|
||||||
|
}
|
||||||
|
} else if transactions[i].TransactionType == "SALES" {
|
||||||
|
futureBalance -= transactions[i].TotalPrice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasPayment || hasPartialPaymentFromBalance {
|
||||||
|
return "DIBAYAR SEBAGIAN", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "BELUM LUNAS", nil
|
||||||
|
}
|
||||||
|
|
||||||
func mapRecordingToProductionResultDTO(record entity.Recording) dto.ProductionResultDTO {
|
func mapRecordingToProductionResultDTO(record entity.Recording) dto.ProductionResultDTO {
|
||||||
result := dto.ProductionResultDTO{
|
result := dto.ProductionResultDTO{
|
||||||
CreatedAt: record.CreatedAt,
|
CreatedAt: record.CreatedAt,
|
||||||
@@ -1057,10 +1267,37 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
costRows, supplierRows, err := s.HppPerKandangRepo.GetFeedOvkDocCostByPeriod(ctx.Context(), startOfDay, endOfDay, params.AreaIDs, params.LocationIDs, params.KandangIDs)
|
|
||||||
if err != nil {
|
validPfkIDs := make([]uint, 0, len(repoRows))
|
||||||
return nil, nil, err
|
pfkIndex := make(map[uint]int, len(repoRows))
|
||||||
|
for idx := range repoRows {
|
||||||
|
row := repoRows[idx]
|
||||||
|
pfkIndex[row.ProjectFlockKandangID] = idx
|
||||||
|
if row.RecordingCount > 0 {
|
||||||
|
validPfkIDs = append(validPfkIDs, row.ProjectFlockKandangID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
costRows := make([]repportRepo.HppPerKandangCostRow, 0)
|
||||||
|
supplierRows := make([]repportRepo.HppPerKandangSupplierRow, 0)
|
||||||
|
if len(validPfkIDs) > 0 {
|
||||||
|
costRows, supplierRows, err = s.HppPerKandangRepo.GetFeedOvkDocCostByPeriod(ctx.Context(), startOfDay, endOfDay, validPfkIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
eggMap, err := s.HppPerKandangRepo.GetEggProductionByProjectFlockKandangIDs(ctx.Context(), startOfDay, endOfDay, validPfkIDs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
for pfkID, egg := range eggMap {
|
||||||
|
if rowIdx, ok := pfkIndex[pfkID]; ok {
|
||||||
|
repoRows[rowIdx].EggProductionWeightKg = egg.EggProductionWeightKg
|
||||||
|
repoRows[rowIdx].EggProductionPieces = egg.EggProductionPieces
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
costMap := make(map[uint]HppCostAggregate, len(costRows))
|
costMap := make(map[uint]HppCostAggregate, len(costRows))
|
||||||
for _, row := range costRows {
|
for _, row := range costRows {
|
||||||
costMap[row.KandangID] = HppCostAggregate{
|
costMap[row.KandangID] = HppCostAggregate{
|
||||||
@@ -1113,9 +1350,15 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
Max float64
|
Max float64
|
||||||
}
|
}
|
||||||
type weightRangeAggregate struct {
|
type weightRangeAggregate struct {
|
||||||
Summary *dto.HppPerKandangSummaryWeightRangeDTO
|
Summary *dto.HppPerKandangSummaryWeightRangeDTO
|
||||||
EggHppSum float64
|
RemainingBirds int64
|
||||||
EggHppCount int
|
RemainingWeightKg float64
|
||||||
|
AvgWeightSum float64
|
||||||
|
AvgWeightCount int64
|
||||||
|
EggHppSum float64
|
||||||
|
EggHppCount int
|
||||||
|
FeedSuppliers map[int64]dto.HppPerKandangSupplierDTO
|
||||||
|
DocSuppliers map[int64]dto.HppPerKandangSupplierDTO
|
||||||
}
|
}
|
||||||
|
|
||||||
dataRows := make([]dto.HppPerKandangRowDTO, 0, len(repoRows))
|
dataRows := make([]dto.HppPerKandangRowDTO, 0, len(repoRows))
|
||||||
@@ -1132,8 +1375,14 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
var totalDocPriceCount int
|
var totalDocPriceCount int
|
||||||
var totalEggHppSum float64
|
var totalEggHppSum float64
|
||||||
var totalEggHppCount int
|
var totalEggHppCount int
|
||||||
|
var totalAvgWeightSum float64
|
||||||
|
var totalAvgWeightCount int64
|
||||||
|
|
||||||
for _, row := range repoRows {
|
for _, row := range repoRows {
|
||||||
|
if !params.ShowUnrecorded && row.RecordingCount == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
birdsFloat := row.RemainingChickenBirds
|
birdsFloat := row.RemainingChickenBirds
|
||||||
if math.IsNaN(birdsFloat) || math.IsInf(birdsFloat, 0) {
|
if math.IsNaN(birdsFloat) || math.IsInf(birdsFloat, 0) {
|
||||||
birdsFloat = 0
|
birdsFloat = 0
|
||||||
@@ -1152,9 +1401,16 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
}
|
}
|
||||||
|
|
||||||
avgWeight := 0.0
|
avgWeight := 0.0
|
||||||
if birdsFloat > 0 {
|
if eggPiecesFloat > 0 {
|
||||||
avgWeight = weightFloat / birdsFloat
|
avgWeight = eggWeightFloat / eggPiecesFloat
|
||||||
}
|
}
|
||||||
|
if params.WeightMin != nil && avgWeight < *params.WeightMin {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if params.WeightMax != nil && avgWeight > *params.WeightMax {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
weightMin := math.Floor(avgWeight*10) / 10
|
weightMin := math.Floor(avgWeight*10) / 10
|
||||||
if weightMin < 0 {
|
if weightMin < 0 {
|
||||||
weightMin = 0
|
weightMin = 0
|
||||||
@@ -1201,9 +1457,7 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
WeightMin: weightMin,
|
WeightMin: weightMin,
|
||||||
WeightMax: weightMax,
|
WeightMax: weightMax,
|
||||||
},
|
},
|
||||||
RemainingChickenBirds: rowBirds,
|
AvgWeightKg: avgWeight,
|
||||||
RemainingChickenWeightKg: weightFloat,
|
|
||||||
AvgWeightKg: avgWeight,
|
|
||||||
// FeedCostRp: costEntry.FeedCost,
|
// FeedCostRp: costEntry.FeedCost,
|
||||||
// OvkCostRp: costEntry.OvkCost,
|
// OvkCostRp: costEntry.OvkCost,
|
||||||
DocSuppliers: docSupplierMap[row.KandangID],
|
DocSuppliers: docSupplierMap[row.KandangID],
|
||||||
@@ -1211,10 +1465,10 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
EggProductionPieces: rowEggPieces,
|
EggProductionPieces: rowEggPieces,
|
||||||
EggProductionKg: eggWeightFloat,
|
EggProductionKg: eggWeightFloat,
|
||||||
AverageDocPriceRp: avgDocPrice,
|
AverageDocPriceRp: avgDocPrice,
|
||||||
HppRp: hppRp,
|
// HppRp: hppRp,
|
||||||
EggHppRpPerKg: eggHpp,
|
EggHppRpPerKg: eggHpp,
|
||||||
RemainingValueRp: rowRemainingValue,
|
RemainingValueRp: rowRemainingValue,
|
||||||
EggValueRp: rowEggValue,
|
EggValueRp: rowEggValue,
|
||||||
})
|
})
|
||||||
|
|
||||||
totalBirds += rowBirds
|
totalBirds += rowBirds
|
||||||
@@ -1223,6 +1477,8 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
totalEggKg += eggWeightFloat
|
totalEggKg += eggWeightFloat
|
||||||
totalRemainingValueRp += rowRemainingValue
|
totalRemainingValueRp += rowRemainingValue
|
||||||
totalEggValueRp += rowEggValue
|
totalEggValueRp += rowEggValue
|
||||||
|
totalAvgWeightSum += avgWeight
|
||||||
|
totalAvgWeightCount++
|
||||||
if weightFloat > 0 {
|
if weightFloat > 0 {
|
||||||
totalHppSum += hppRp
|
totalHppSum += hppRp
|
||||||
totalHppCount++
|
totalHppCount++
|
||||||
@@ -1246,13 +1502,27 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
},
|
},
|
||||||
Label: fmt.Sprintf("%.2f - %.2f", weightMin, weightMax),
|
Label: fmt.Sprintf("%.2f - %.2f", weightMin, weightMax),
|
||||||
},
|
},
|
||||||
|
FeedSuppliers: make(map[int64]dto.HppPerKandangSupplierDTO),
|
||||||
|
DocSuppliers: make(map[int64]dto.HppPerKandangSupplierDTO),
|
||||||
}
|
}
|
||||||
perRangeMap[rangeKey] = rangeAgg
|
perRangeMap[rangeKey] = rangeAgg
|
||||||
}
|
}
|
||||||
|
|
||||||
rangeSummary := rangeAgg.Summary
|
rangeSummary := rangeAgg.Summary
|
||||||
rangeSummary.RemainingChickenBirds += rowBirds
|
rangeAgg.RemainingBirds += rowBirds
|
||||||
rangeSummary.RemainingChickenWeightKg += row.RemainingChickenWeight
|
rangeAgg.RemainingWeightKg += row.RemainingChickenWeight
|
||||||
|
rangeAgg.AvgWeightSum += avgWeight
|
||||||
|
rangeAgg.AvgWeightCount++
|
||||||
|
for _, supplier := range feedSupplierMap[row.KandangID] {
|
||||||
|
if _, ok := rangeAgg.FeedSuppliers[supplier.ID]; !ok {
|
||||||
|
rangeAgg.FeedSuppliers[supplier.ID] = supplier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, supplier := range docSupplierMap[row.KandangID] {
|
||||||
|
if _, ok := rangeAgg.DocSuppliers[supplier.ID]; !ok {
|
||||||
|
rangeAgg.DocSuppliers[supplier.ID] = supplier
|
||||||
|
}
|
||||||
|
}
|
||||||
rangeSummary.EggProductionPieces += rowEggPieces
|
rangeSummary.EggProductionPieces += rowEggPieces
|
||||||
rangeSummary.EggProductionKg += eggWeightFloat
|
rangeSummary.EggProductionKg += eggWeightFloat
|
||||||
rangeSummary.RemainingValueRp += rowRemainingValue
|
rangeSummary.RemainingValueRp += rowRemainingValue
|
||||||
@@ -1279,31 +1549,37 @@ func (s *repportService) GetHppPerKandang(ctx *fiber.Ctx) (*dto.HppPerKandangRes
|
|||||||
agg := perRangeMap[key]
|
agg := perRangeMap[key]
|
||||||
entry := agg.Summary
|
entry := agg.Summary
|
||||||
entry.ID = idx + 1
|
entry.ID = idx + 1
|
||||||
if entry.RemainingChickenBirds > 0 {
|
if agg.AvgWeightCount > 0 {
|
||||||
entry.AvgWeightKg = entry.RemainingChickenWeightKg / float64(entry.RemainingChickenBirds)
|
entry.AvgWeightKg = agg.AvgWeightSum / float64(agg.AvgWeightCount)
|
||||||
}
|
}
|
||||||
if agg.EggHppCount > 0 {
|
if agg.EggHppCount > 0 {
|
||||||
entry.EggHppRpPerKg = agg.EggHppSum / float64(agg.EggHppCount)
|
entry.EggHppRpPerKg = agg.EggHppSum / float64(agg.EggHppCount)
|
||||||
}
|
}
|
||||||
|
entry.FeedSuppliers = make([]dto.HppPerKandangSupplierDTO, 0, len(agg.FeedSuppliers))
|
||||||
|
for _, supplier := range agg.FeedSuppliers {
|
||||||
|
entry.FeedSuppliers = append(entry.FeedSuppliers, supplier)
|
||||||
|
}
|
||||||
|
entry.DocSuppliers = make([]dto.HppPerKandangSupplierDTO, 0, len(agg.DocSuppliers))
|
||||||
|
for _, supplier := range agg.DocSuppliers {
|
||||||
|
entry.DocSuppliers = append(entry.DocSuppliers, supplier)
|
||||||
|
}
|
||||||
perRangeSummary = append(perRangeSummary, *entry)
|
perRangeSummary = append(perRangeSummary, *entry)
|
||||||
}
|
}
|
||||||
|
|
||||||
totalSummary := dto.HppPerKandangSummaryTotalDTO{
|
totalSummary := dto.HppPerKandangSummaryTotalDTO{
|
||||||
TotalRemainingChickenBirds: totalBirds,
|
TotalEggProductionPieces: totalEggPieces,
|
||||||
TotalRemainingChickenWeightKg: totalWeight,
|
TotalEggProductionKg: totalEggKg,
|
||||||
TotalEggProductionPieces: totalEggPieces,
|
TotalEggValueRp: totalEggValueRp,
|
||||||
TotalEggProductionKg: totalEggKg,
|
|
||||||
TotalRemainingValueRp: totalRemainingValueRp,
|
|
||||||
TotalEggValueRp: totalEggValueRp,
|
|
||||||
}
|
}
|
||||||
if totalBirds > 0 {
|
if totalBirds > 0 {
|
||||||
totalSummary.AverageWeightKg = totalWeight / float64(totalBirds)
|
}
|
||||||
|
if totalAvgWeightCount > 0 {
|
||||||
|
totalSummary.AverageWeightKg = totalAvgWeightSum / float64(totalAvgWeightCount)
|
||||||
}
|
}
|
||||||
if totalEggHppCount > 0 {
|
if totalEggHppCount > 0 {
|
||||||
totalSummary.AverageEggHppRpPerKg = totalEggHppSum / float64(totalEggHppCount)
|
totalSummary.AverageEggHppRpPerKg = totalEggHppSum / float64(totalEggHppCount)
|
||||||
}
|
}
|
||||||
if totalHppCount > 0 {
|
if totalHppCount > 0 {
|
||||||
totalSummary.TotalHppRp = totalHppSum / float64(totalHppCount)
|
|
||||||
}
|
}
|
||||||
if totalDocPriceCount > 0 {
|
if totalDocPriceCount > 0 {
|
||||||
totalSummary.TotalAverageDocPriceRp = totalDocPriceSum / float64(totalDocPriceCount)
|
totalSummary.TotalAverageDocPriceRp = totalDocPriceSum / float64(totalDocPriceCount)
|
||||||
|
|||||||
@@ -71,3 +71,11 @@ type ProductionResultQuery struct {
|
|||||||
Limit int `query:"limit" validate:"omitempty,min=1,max=100,gt=0"`
|
Limit int `query:"limit" validate:"omitempty,min=1,max=100,gt=0"`
|
||||||
ProjectFlockKandangID uint `query:"-" validate:"required,gt=0"`
|
ProjectFlockKandangID uint `query:"-" validate:"required,gt=0"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type CustomerPaymentQuery struct {
|
||||||
|
Page int `query:"page" validate:"omitempty,min=1,gt=0"`
|
||||||
|
Limit int `query:"limit" validate:"omitempty,min=1,max=100,gt=0"`
|
||||||
|
CustomerIDs []uint `query:"customer_ids" validate:"omitempty,dive,gt=0"`
|
||||||
|
StartDate string `query:"start_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
|
EndDate string `query:"end_date" validate:"omitempty,datetime=2006-01-02"`
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user