package transformer import ( "apskel-pos-be/internal/contract" "apskel-pos-be/internal/models" "apskel-pos-be/internal/util" "fmt" "time" "github.com/google/uuid" ) // parseOutletID converts a *string outlet ID to *uuid.UUID, returning nil for invalid/empty values. func parseOutletID(s *string) *uuid.UUID { if s == nil { return nil } id, err := uuid.Parse(*s) if err != nil { return nil } return &id } // PaymentMethodAnalyticsContractToModel converts contract request to model func PaymentMethodAnalyticsContractToModel(req *contract.PaymentMethodAnalyticsRequest) *models.PaymentMethodAnalyticsRequest { var dateFrom, dateTo time.Time if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.PaymentMethodAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, GroupBy: req.GroupBy, } } // PaymentMethodAnalyticsModelToContract converts model response to contract func PaymentMethodAnalyticsModelToContract(resp *models.PaymentMethodAnalyticsResponse) *contract.PaymentMethodAnalyticsResponse { if resp == nil { return nil } var data []contract.PaymentMethodAnalyticsData for _, item := range resp.Data { data = append(data, contract.PaymentMethodAnalyticsData{ PaymentMethodID: item.PaymentMethodID, PaymentMethodName: item.PaymentMethodName, PaymentMethodType: item.PaymentMethodType, TotalAmount: item.TotalAmount, OrderCount: item.OrderCount, PaymentCount: item.PaymentCount, Percentage: item.Percentage, }) } return &contract.PaymentMethodAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, GroupBy: resp.GroupBy, Summary: contract.PaymentMethodSummary{ TotalAmount: resp.Summary.TotalAmount, TotalOrders: resp.Summary.TotalOrders, TotalPayments: resp.Summary.TotalPayments, AverageOrderValue: resp.Summary.AverageOrderValue, }, Data: data, } } func SalesAnalyticsContractToModel(req *contract.SalesAnalyticsRequest) *models.SalesAnalyticsRequest { var dateFrom, dateTo time.Time if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.SalesAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, GroupBy: req.GroupBy, } } // SalesAnalyticsModelToContract converts model response to contract func SalesAnalyticsModelToContract(resp *models.SalesAnalyticsResponse) *contract.SalesAnalyticsResponse { if resp == nil { return nil } var data []contract.SalesAnalyticsData for _, item := range resp.Data { data = append(data, contract.SalesAnalyticsData{ Date: item.Date, Sales: item.Sales, Orders: item.Orders, Items: item.Items, Tax: item.Tax, Discount: item.Discount, NetSales: item.NetSales, }) } return &contract.SalesAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, GroupBy: resp.GroupBy, Summary: contract.SalesSummary{ TotalSales: resp.Summary.TotalSales, TotalOrders: resp.Summary.TotalOrders, TotalItems: resp.Summary.TotalItems, AverageOrderValue: resp.Summary.AverageOrderValue, TotalTax: resp.Summary.TotalTax, TotalDiscount: resp.Summary.TotalDiscount, NetSales: resp.Summary.NetSales, }, Data: data, } } // PurchasingAnalyticsContractToModel converts contract request to model func PurchasingAnalyticsContractToModel(req *contract.PurchasingAnalyticsRequest) *models.PurchasingAnalyticsRequest { var dateFrom, dateTo time.Time if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.PurchasingAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, GroupBy: req.GroupBy, } } // PurchasingAnalyticsModelToContract converts model response to contract func PurchasingAnalyticsModelToContract(resp *models.PurchasingAnalyticsResponse) *contract.PurchasingAnalyticsResponse { if resp == nil { return nil } data := make([]contract.PurchasingAnalyticsData, len(resp.Data)) for i, item := range resp.Data { data[i] = contract.PurchasingAnalyticsData{ Date: item.Date, Purchases: item.Purchases, RawMaterialPurchases: item.RawMaterialPurchases, ExpensePurchases: item.ExpensePurchases, PurchaseOrders: item.PurchaseOrders, RawMaterialPurchaseOrders: item.RawMaterialPurchaseOrders, ExpenseCount: item.ExpenseCount, Quantity: item.Quantity, Ingredients: item.Ingredients, Vendors: item.Vendors, } } ingredientData := make([]contract.PurchasingIngredientData, len(resp.IngredientData)) for i, item := range resp.IngredientData { ingredientData[i] = contract.PurchasingIngredientData{ IngredientID: item.IngredientID, IngredientName: item.IngredientName, Quantity: item.Quantity, TotalCost: item.TotalCost, AverageUnitCost: item.AverageUnitCost, PurchaseOrderCount: item.PurchaseOrderCount, } } vendorData := make([]contract.PurchasingVendorData, len(resp.VendorData)) for i, item := range resp.VendorData { vendorData[i] = contract.PurchasingVendorData{ VendorID: item.VendorID, VendorName: item.VendorName, TotalCost: item.TotalCost, PurchaseOrderCount: item.PurchaseOrderCount, IngredientCount: item.IngredientCount, Quantity: item.Quantity, } } return &contract.PurchasingAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, OutletName: resp.OutletName, DateFrom: resp.DateFrom, DateTo: resp.DateTo, GroupBy: resp.GroupBy, Summary: contract.PurchasingSummary{ TotalPurchases: resp.Summary.TotalPurchases, RawMaterialPurchases: resp.Summary.RawMaterialPurchases, ExpensePurchases: resp.Summary.ExpensePurchases, TotalPurchaseOrders: resp.Summary.TotalPurchaseOrders, RawMaterialPurchaseOrders: resp.Summary.RawMaterialPurchaseOrders, ExpenseCount: resp.Summary.ExpenseCount, TotalQuantity: resp.Summary.TotalQuantity, AveragePurchaseOrderValue: resp.Summary.AveragePurchaseOrderValue, TotalIngredients: resp.Summary.TotalIngredients, TotalVendors: resp.Summary.TotalVendors, }, Data: data, IngredientData: ingredientData, VendorData: vendorData, } } // ProductAnalyticsContractToModel converts contract request to model func ProductAnalyticsContractToModel(req *contract.ProductAnalyticsRequest) *models.ProductAnalyticsRequest { var dateFrom, dateTo time.Time if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.ProductAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, Limit: req.Limit, } } // ProductAnalyticsModelToContract converts model response to contract func ProductAnalyticsModelToContract(resp *models.ProductAnalyticsResponse) *contract.ProductAnalyticsResponse { if resp == nil { return nil } var data []contract.ProductAnalyticsData for _, item := range resp.Data { data = append(data, contract.ProductAnalyticsData{ ProductID: item.ProductID, ProductName: item.ProductName, ProductSku: item.ProductSku, ProductPrice: item.ProductPrice, CategoryID: item.CategoryID, CategoryName: item.CategoryName, CategoryOrder: item.CategoryOrder, QuantitySold: item.QuantitySold, Revenue: item.Revenue, AveragePrice: item.AveragePrice, OrderCount: item.OrderCount, StandardHppPerUnit: item.StandardHppPerUnit, StandardHppTotal: item.StandardHppTotal, FifoHppPerUnit: item.FifoHppPerUnit, FifoHppTotal: item.FifoHppTotal, MovingAverageHppPerUnit: item.MovingAverageHppPerUnit, MovingAverageHppTotal: item.MovingAverageHppTotal, }) } return &contract.ProductAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, Data: data, } } // ProductAnalyticsPerCategoryContractToModel converts contract request to model func ProductAnalyticsPerCategoryContractToModel(req *contract.ProductAnalyticsPerCategoryRequest) *models.ProductAnalyticsPerCategoryRequest { var dateFrom, dateTo time.Time // Parse date range using utility function if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.ProductAnalyticsPerCategoryRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, } } // ProductAnalyticsPerCategoryModelToContract converts model response to contract func ProductAnalyticsPerCategoryModelToContract(resp *models.ProductAnalyticsPerCategoryResponse) *contract.ProductAnalyticsPerCategoryResponse { if resp == nil { return nil } var data []contract.ProductAnalyticsPerCategoryData for _, item := range resp.Data { data = append(data, contract.ProductAnalyticsPerCategoryData{ CategoryID: item.CategoryID, CategoryName: item.CategoryName, TotalRevenue: item.TotalRevenue, TotalQuantity: item.TotalQuantity, ProductCount: item.ProductCount, OrderCount: item.OrderCount, TotalStandardHpp: item.TotalStandardHpp, TotalFifoHpp: item.TotalFifoHpp, TotalMovingAverageHpp: item.TotalMovingAverageHpp, }) } return &contract.ProductAnalyticsPerCategoryResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, Data: data, } } // DashboardAnalyticsContractToModel converts contract request to model func DashboardAnalyticsContractToModel(req *contract.DashboardAnalyticsRequest) *models.DashboardAnalyticsRequest { var dateFrom, dateTo time.Time // Parse date range using utility function if fromTime, toTime, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo); err == nil { if fromTime != nil { dateFrom = *fromTime } if toTime != nil { dateTo = *toTime } } return &models.DashboardAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: dateFrom, DateTo: dateTo, } } // DashboardAnalyticsModelToContract converts model response to contract func DashboardAnalyticsModelToContract(resp *models.DashboardAnalyticsResponse) *contract.DashboardAnalyticsResponse { if resp == nil { return nil } var topProducts []contract.ProductAnalyticsData for _, item := range resp.TopProducts { topProducts = append(topProducts, contract.ProductAnalyticsData{ ProductID: item.ProductID, ProductName: item.ProductName, ProductPrice: item.ProductPrice, CategoryID: item.CategoryID, CategoryName: item.CategoryName, QuantitySold: item.QuantitySold, Revenue: item.Revenue, AveragePrice: item.AveragePrice, OrderCount: item.OrderCount, StandardHppPerUnit: item.StandardHppPerUnit, StandardHppTotal: item.StandardHppTotal, FifoHppPerUnit: item.FifoHppPerUnit, FifoHppTotal: item.FifoHppTotal, MovingAverageHppPerUnit: item.MovingAverageHppPerUnit, MovingAverageHppTotal: item.MovingAverageHppTotal, }) } var paymentMethods []contract.PaymentMethodAnalyticsData for _, item := range resp.PaymentMethods { paymentMethods = append(paymentMethods, contract.PaymentMethodAnalyticsData{ PaymentMethodID: item.PaymentMethodID, PaymentMethodName: item.PaymentMethodName, PaymentMethodType: item.PaymentMethodType, TotalAmount: item.TotalAmount, OrderCount: item.OrderCount, PaymentCount: item.PaymentCount, Percentage: item.Percentage, }) } var recentSales []contract.SalesAnalyticsData for _, item := range resp.RecentSales { recentSales = append(recentSales, contract.SalesAnalyticsData{ Date: item.Date, Sales: item.Sales, Orders: item.Orders, Items: item.Items, Tax: item.Tax, Discount: item.Discount, NetSales: item.NetSales, }) } return &contract.DashboardAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, Overview: contract.DashboardOverview{ TotalSales: resp.Overview.TotalSales, TotalOrders: resp.Overview.TotalOrders, AverageOrderValue: resp.Overview.AverageOrderValue, TotalCustomers: resp.Overview.TotalCustomers, VoidedOrders: resp.Overview.VoidedOrders, RefundedOrders: resp.Overview.RefundedOrders, }, TopProducts: topProducts, PaymentMethods: paymentMethods, RecentSales: recentSales, } } func ProfitLossAnalyticsContractToModel(req *contract.ProfitLossAnalyticsRequest) (*models.ProfitLossAnalyticsRequest, error) { if req == nil { return nil, fmt.Errorf("request cannot be nil") } dateFrom, dateTo, err := util.ParseDateRangeToJakartaTime(req.DateFrom, req.DateTo) if err != nil { return nil, fmt.Errorf("invalid date range: %w", err) } if dateFrom == nil { return nil, fmt.Errorf("date_from is required") } if dateTo == nil { return nil, fmt.Errorf("date_to is required") } return &models.ProfitLossAnalyticsRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: *dateFrom, DateTo: *dateTo, GroupBy: req.GroupBy, }, nil } func ProfitLossAnalyticsModelToContract(resp *models.ProfitLossAnalyticsResponse) *contract.ProfitLossAnalyticsResponse { if resp == nil { return nil } mainSummary := make([]contract.ProfitLossSummaryRow, len(resp.MainSummary)) for i, row := range resp.MainSummary { mainSummary[i] = profitLossSummaryRowModelToContract(row) } data := make([]contract.ProfitLossData, len(resp.Data)) for i, item := range resp.Data { data[i] = contract.ProfitLossData{ Date: item.Date, Revenue: item.Revenue, Cost: item.Cost, GrossProfit: item.GrossProfit, GrossProfitMargin: item.GrossProfitMargin, Tax: item.Tax, Discount: item.Discount, NetProfit: item.NetProfit, NetProfitMargin: item.NetProfitMargin, Orders: item.Orders, } } productData := make([]contract.ProductProfitData, len(resp.ProductData)) for i, item := range resp.ProductData { productData[i] = contract.ProductProfitData{ ProductID: item.ProductID, ProductName: item.ProductName, CategoryID: item.CategoryID, CategoryName: item.CategoryName, QuantitySold: item.QuantitySold, Revenue: item.Revenue, Cost: item.Cost, GrossProfit: item.GrossProfit, GrossProfitMargin: item.GrossProfitMargin, AveragePrice: item.AveragePrice, AverageCost: item.AverageCost, ProfitPerUnit: item.ProfitPerUnit, } } opsItems := make([]contract.OperationalExpenseItem, len(resp.OperationalExpenses)) for i, item := range resp.OperationalExpenses { opsItems[i] = contract.OperationalExpenseItem{ Item: item.Item, Nominal: item.Nominal, } } return &contract.ProfitLossAnalyticsResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, DateFrom: resp.DateFrom, DateTo: resp.DateTo, GroupBy: resp.GroupBy, Summary: contract.ProfitLossSummary{ TotalRevenue: resp.Summary.TotalRevenue, TotalCost: resp.Summary.TotalCost, GrossProfit: resp.Summary.GrossProfit, GrossProfitMargin: resp.Summary.GrossProfitMargin, TotalTax: resp.Summary.TotalTax, TotalDiscount: resp.Summary.TotalDiscount, NetProfit: resp.Summary.NetProfit, NetProfitMargin: resp.Summary.NetProfitMargin, TotalOrders: resp.Summary.TotalOrders, AverageProfit: resp.Summary.AverageProfit, ProfitabilityRatio: resp.Summary.ProfitabilityRatio, }, Data: data, ProductData: productData, MainSummary: mainSummary, OperationalExpenses: opsItems, OperationalExpensesTotal: resp.OperationalExpensesTotal, } } func profitLossSummaryRowModelToContract(row models.ProfitLossSummaryRow) contract.ProfitLossSummaryRow { subItems := make([]contract.ProfitLossSummaryRow, len(row.SubItems)) for i, sub := range row.SubItems { subItems[i] = profitLossSummaryRowModelToContract(sub) } return contract.ProfitLossSummaryRow{ ID: row.ID, Label: row.Label, IsBold: row.IsBold, TodayNominal: row.TodayNominal, TodayPct: row.TodayPct, MtdNominal: row.MtdNominal, MtdPct: row.MtdPct, SubItems: subItems, } } func ExclusiveSummaryPeriodContractToModel(req *contract.ExclusiveSummaryPeriodRequest) (*models.ExclusiveSummaryPeriodRequest, error) { if req == nil { return nil, fmt.Errorf("request cannot be nil") } dateFrom, dateTo, err := parseFlexibleDateRangeToJakartaTime(req.DateFrom, req.DateTo) if err != nil { return nil, fmt.Errorf("invalid date range: %w", err) } if dateFrom == nil { return nil, fmt.Errorf("date_from is required") } if dateTo == nil { return nil, fmt.Errorf("date_to is required") } return &models.ExclusiveSummaryPeriodRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateFrom: *dateFrom, DateTo: *dateTo, ExcludeGajiStaffFromReimburse: req.ExcludeGajiStaffFromReimburse, }, nil } func ExclusiveSummaryMonthlyContractToModel(req *contract.ExclusiveSummaryMonthlyRequest) (*models.ExclusiveSummaryMonthlyRequest, error) { if req == nil { return nil, fmt.Errorf("request cannot be nil") } month, err := parseMonthToJakartaTime(req.Month) if err != nil { return nil, fmt.Errorf("invalid month: %w", err) } return &models.ExclusiveSummaryMonthlyRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), Month: month, }, nil } func ExclusiveSummaryMTDContractToModel(req *contract.ExclusiveSummaryMTDRequest) (*models.ExclusiveSummaryMTDRequest, error) { if req == nil { return nil, fmt.Errorf("request cannot be nil") } dateTo, err := parseFlexibleDateToJakartaTime(req.DateTo, true) if err != nil { return nil, fmt.Errorf("invalid date_to: %w", err) } if dateTo == nil { return nil, fmt.Errorf("date_to is required") } return &models.ExclusiveSummaryMTDRequest{ OrganizationID: req.OrganizationID, OutletID: parseOutletID(req.OutletID), DateTo: *dateTo, ExcludeGajiStaffFromReimburse: req.ExcludeGajiStaffFromReimburse, }, nil } func ExclusiveSummaryPeriodModelToContract(resp *models.ExclusiveSummaryPeriodResponse) *contract.ExclusiveSummaryPeriodResponse { if resp == nil { return nil } hppBreakdown := make([]contract.ExclusiveSummaryCategoryBreakdown, len(resp.HPPBreakdown)) for i, item := range resp.HPPBreakdown { hppBreakdown[i] = exclusiveSummaryCategoryBreakdownModelToContract(item) } operationalBreakdown := make([]contract.ExclusiveSummaryCategoryBreakdown, len(resp.OperationalExpenseBreakdown)) for i, item := range resp.OperationalExpenseBreakdown { operationalBreakdown[i] = exclusiveSummaryCategoryBreakdownModelToContract(item) } dailySummary := make([]contract.ExclusiveSummaryDailySummary, len(resp.DailySummary)) for i, item := range resp.DailySummary { dailySummary[i] = contract.ExclusiveSummaryDailySummary{ Date: item.Date, TransactionCount: item.TransactionCount, TotalCost: item.TotalCost, } } dailyTransactions := make([]contract.ExclusiveSummaryDailyTransaction, len(resp.DailyTransactions)) for i, item := range resp.DailyTransactions { dailyTransactions[i] = contract.ExclusiveSummaryDailyTransaction{ Date: item.Date, CategoryCode: item.CategoryCode, CategoryName: item.CategoryName, Description: item.Description, Amount: item.Amount, Source: item.Source, } } return &contract.ExclusiveSummaryPeriodResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, Period: contract.ExclusiveSummaryPeriodRange{ DateFrom: resp.Period.DateFrom, DateTo: resp.Period.DateTo, }, Summary: contract.ExclusiveSummaryPeriodSummary{ Sales: resp.Summary.Sales, HPP: resp.Summary.HPP, GrossProfit: resp.Summary.GrossProfit, SalaryTotal: resp.Summary.SalaryTotal, SalaryDW: resp.Summary.SalaryDW, SalaryStaff: resp.Summary.SalaryStaff, SalaryOther: resp.Summary.SalaryOther, OtherOperationalExpenses: resp.Summary.OtherOperationalExpenses, OperationalExpensesTotal: resp.Summary.OperationalExpensesTotal, TotalCost: resp.Summary.TotalCost, NetProfit: resp.Summary.NetProfit, }, Reimburse: contract.ExclusiveSummaryReimburse{ TotalCost: resp.Reimburse.TotalCost, ExcludedSalaryStaff: resp.Reimburse.ExcludedSalaryStaff, TotalReimburse: resp.Reimburse.TotalReimburse, }, HPPBreakdown: hppBreakdown, OperationalExpenseBreakdown: operationalBreakdown, DailySummary: dailySummary, DailyTransactions: dailyTransactions, } } func ExclusiveSummaryMonthlyModelToContract(resp *models.ExclusiveSummaryMonthlyResponse) *contract.ExclusiveSummaryMonthlyResponse { if resp == nil { return nil } periods := make([]contract.ExclusiveSummaryMonthlyPeriod, len(resp.Periods)) for i, item := range resp.Periods { periods[i] = contract.ExclusiveSummaryMonthlyPeriod{ Label: item.Label, DateFrom: item.DateFrom, DateTo: item.DateTo, Sales: item.Sales, HPP: item.HPP, GrossProfit: item.GrossProfit, GrossMargin: item.GrossMargin, } } bankBalance := make([]contract.ExclusiveSummaryBankBalance, len(resp.BankBalance)) for i, item := range resp.BankBalance { bankBalance[i] = contract.ExclusiveSummaryBankBalance{ Bank: item.Bank, OpeningBalance: item.OpeningBalance, IncomingMutation: item.IncomingMutation, OutgoingMutation: item.OutgoingMutation, ClosingBalance: item.ClosingBalance, Notes: item.Notes, } } return &contract.ExclusiveSummaryMonthlyResponse{ OrganizationID: resp.OrganizationID, OutletID: resp.OutletID, Month: resp.Month, Summary: contract.ExclusiveSummaryMonthlySummary{ TotalSales: resp.Summary.TotalSales, HPP: resp.Summary.HPP, GrossProfit: resp.Summary.GrossProfit, OperationalExpensesTotal: resp.Summary.OperationalExpensesTotal, TotalCost: resp.Summary.TotalCost, NetProfit: resp.Summary.NetProfit, NetProfitMargin: resp.Summary.NetProfitMargin, }, Periods: periods, BankBalance: bankBalance, } } func exclusiveSummaryCategoryBreakdownModelToContract(item models.ExclusiveSummaryCategoryBreakdown) contract.ExclusiveSummaryCategoryBreakdown { return contract.ExclusiveSummaryCategoryBreakdown{ CategoryCode: item.CategoryCode, CategoryName: item.CategoryName, Amount: item.Amount, Percentage: item.Percentage, } } func parseFlexibleDateRangeToJakartaTime(dateFrom, dateTo string) (*time.Time, *time.Time, error) { fromTime, toTime, err := util.ParseDateRangeToJakartaTime(dateFrom, dateTo) if err == nil { return fromTime, toTime, nil } fromTime, err = parseISODateToJakartaTime(dateFrom, false) if err != nil { return nil, nil, err } toTime, err = parseISODateToJakartaTime(dateTo, true) if err != nil { return nil, nil, err } return fromTime, toTime, nil } func parseISODateToJakartaTime(dateStr string, endOfDay bool) (*time.Time, error) { if dateStr == "" { return nil, nil } date, err := time.Parse("2006-01-02", dateStr) if err != nil { return nil, err } location, err := time.LoadLocation("Asia/Jakarta") if err != nil { return nil, err } if endOfDay { result := time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, 999999999, location) return &result, nil } result := time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, location) return &result, nil } func parseFlexibleDateToJakartaTime(dateStr string, endOfDay bool) (*time.Time, error) { if dateStr == "" { return nil, nil } fromTime, toTime, err := util.ParseDateRangeToJakartaTime(dateStr, dateStr) if err == nil { if endOfDay { return toTime, nil } return fromTime, nil } return parseISODateToJakartaTime(dateStr, endOfDay) } func parseMonthToJakartaTime(month string) (time.Time, error) { location, err := time.LoadLocation("Asia/Jakarta") if err != nil { return time.Time{}, err } parsed, err := time.ParseInLocation("2006-01", month, location) if err != nil { return time.Time{}, err } return time.Date(parsed.Year(), parsed.Month(), 1, 0, 0, 0, 0, location), nil }