501 lines
16 KiB
Go
501 lines
16 KiB
Go
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,
|
|
PurchaseOrders: item.PurchaseOrders,
|
|
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,
|
|
TotalPurchaseOrders: resp.Summary.TotalPurchaseOrders,
|
|
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,
|
|
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,
|
|
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,
|
|
}, 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)
|
|
}
|
|
|
|
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,
|
|
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,
|
|
}
|
|
}
|