2023-10-08 15:59:42 +07:00
package orders
import (
"context"
"furtuna-be/internal/common/logger"
2024-07-27 16:47:33 +07:00
"furtuna-be/internal/common/mycontext"
2023-10-08 15:59:42 +07:00
"furtuna-be/internal/entity"
2024-07-26 11:37:22 +07:00
"strings"
2024-07-27 16:47:33 +07:00
"time"
2024-07-26 11:37:22 +07:00
2023-10-08 15:59:42 +07:00
"go.uber.org/zap"
"gorm.io/gorm"
)
type OrderRepository struct {
db * gorm . DB
}
func NewOrderRepository ( db * gorm . DB ) * OrderRepository {
return & OrderRepository {
db : db ,
}
}
2024-06-04 02:59:31 +07:00
func ( r * OrderRepository ) Create ( ctx context . Context , order * entity . Order ) ( * entity . Order , error ) {
err := r . db . WithContext ( ctx ). Create ( order ). Error
if err != nil {
logger . ContextLogger ( ctx ). Error ( "error when creating order" , zap . Error ( err ))
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-05 00:24:53 +07:00
return r . FindByID ( ctx , order . ID )
2023-10-08 15:59:42 +07:00
}
2024-06-04 02:59:31 +07:00
func ( r * OrderRepository ) UpdateStatus ( ctx context . Context , orderID int64 , status string ) ( * entity . Order , error ) {
order := new ( entity . Order )
if err := r . db . WithContext ( ctx ). First ( order , orderID ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when finding order" , zap . Error ( err ))
return nil , err
}
order . Status = status
if err := r . db . WithContext ( ctx ). Save ( order ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when updating order status" , zap . Error ( err ))
2023-10-08 15:59:42 +07:00
return nil , err
}
return order , nil
}
2024-06-04 02:59:31 +07:00
func ( r * OrderRepository ) FindByID ( ctx context . Context , id int64 ) ( * entity . Order , error ) {
var order entity . Order
2024-06-05 00:24:53 +07:00
2024-08-04 01:14:59 +07:00
err := r . db . WithContext ( ctx ).
Preload ( "OrderItems" , func ( db * gorm . DB ) * gorm . DB {
return db . Preload ( "Product" )
}).
2024-08-21 22:27:05 +07:00
Preload ( "Site" ).
2024-08-04 01:14:59 +07:00
Preload ( "User" ).
Preload ( "Payment" ).
First ( & order , id ). Error
2024-06-05 00:24:53 +07:00
if err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ). Error ( "error when finding order by ID" , zap . Error ( err ))
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-05 00:24:53 +07:00
2024-06-04 02:59:31 +07:00
return & order , nil
2023-10-08 15:59:42 +07:00
}
2024-08-21 15:48:50 +07:00
func ( r * OrderRepository ) FindPrintDetailByID ( ctx context . Context , id int64 ) ( * entity . OrderPrintDetail , error ) {
var printDetail entity . OrderPrintDetail
err := r . db . WithContext ( ctx ).
Table ( "orders" ).
2024-08-24 14:25:58 +07:00
Select ( "orders.id, partners.name as partner_name, sites.name as site_name, partners.logo as logo, orders.ref_id as order_id, " +
2024-08-21 15:48:50 +07:00
"orders.visit_date, orders.payment_type, orders.source, " +
"orders.ticket_status, orders.total, orders.fee" ).
Joins ( "JOIN partners ON partners.id = orders.partner_id" ).
2024-08-24 14:25:58 +07:00
Joins ( "JOIN sites ON sites.id = orders.site_id" ).
2024-08-21 15:48:50 +07:00
Where ( "orders.id = ?" , id ).
Scan ( & printDetail ). Error
if err == nil {
err = r . db . WithContext ( ctx ). Where ( "order_id = ?" , id ). Preload ( "Product" ). Find ( & printDetail . OrderItems ). Error
}
if err != nil {
logger . ContextLogger ( ctx ). Error ( "error when finding print detail by ID" , zap . Error ( err ))
return nil , err
}
return & printDetail , nil
}
2024-08-13 23:09:05 +07:00
func ( r * OrderRepository ) FindByQRCode ( ctx context . Context , refID string ) ( * entity . Order , error ) {
var order entity . Order
err := r . db . WithContext ( ctx ).
Preload ( "OrderItems" , func ( db * gorm . DB ) * gorm . DB {
return db . Preload ( "Product" )
}).
Preload ( "User" ).
Preload ( "Payment" ).
Where ( "ref_id = ?" , refID ).
First ( & order ). Error
if err != nil {
logger . ContextLogger ( ctx ). Error ( "error when finding order by refID" , zap . Error ( err ))
return nil , err
}
return & order , nil
}
2024-06-05 00:24:53 +07:00
func ( r * OrderRepository ) SetOrderStatus ( ctx context . Context , db * gorm . DB , orderID int64 , status string ) error {
var order entity . Order
if err := db . WithContext ( ctx ). Preload ( "OrderItems" ). First ( & order , orderID ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when finding order by ID" , zap . Error ( err ))
return err
}
order . Status = status
if err := db . WithContext ( ctx ). Save ( & order ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when updating order status" , zap . Error ( err ))
return err
}
return nil
}
2024-06-04 02:59:31 +07:00
func ( r * OrderRepository ) Update ( ctx context . Context , order * entity . Order ) ( * entity . Order , error ) {
2024-08-05 23:58:15 +07:00
if err := r . db . WithContext ( ctx ). Model ( & entity . Order {}). Where ( "id = ?" , order . ID ). Updates ( order ). Error ; err != nil {
2024-06-04 02:59:31 +07:00
logger . ContextLogger ( ctx ). Error ( "error when updating order" , zap . Error ( err ))
2023-10-08 15:59:42 +07:00
return nil , err
}
2024-06-04 02:59:31 +07:00
return order , nil
2023-10-08 15:59:42 +07:00
}
2024-07-26 11:37:22 +07:00
2024-07-27 16:47:33 +07:00
func ( b * OrderRepository ) GetAllHystoryOrders ( ctx context . Context , req entity . OrderSearch ) ( entity . HistoryOrderList , int , error ) {
2024-07-26 11:37:22 +07:00
var orders [] * entity . HistoryOrderDB
var total int64
query := b . db . Table ( "orders" ).
2024-08-10 23:21:01 +07:00
Select ( "orders.id as id, users.name as employee, sites.name as site, orders.created_at as timestamp, orders.created_at as booking_time, STRING_AGG(ticket_summary.name || ' x' || ticket_summary.total_qty, ', ') AS tickets, orders.payment_type as payment_type, orders.status as status, orders.amount as amount, orders.visit_date as visit_date, orders.ticket_status as ticket_status, orders.source as source" ).
2024-07-26 11:37:22 +07:00
Joins ( "left join (SELECT items.order_id, products.name, SUM(items.qty) AS total_qty FROM order_items items LEFT JOIN products ON items.item_id = products.id GROUP BY items.order_id, products.name) AS ticket_summary ON orders.id = ticket_summary.order_id" ).
Joins ( "left join users on orders.created_by = users.id" ).
2024-08-15 22:45:07 +07:00
Joins ( "left join sites on orders.site_id = sites.id" ).
Where ( "orders.payment_type != ?" , "NEW" )
2024-07-30 22:02:36 +07:00
2024-08-13 23:09:05 +07:00
if req . PaymentType != "" {
2024-07-30 22:02:36 +07:00
query = query . Where ( "orders.payment_type = ?" , req . PaymentType )
}
2024-08-04 01:14:59 +07:00
if req . CreatedBy != 0 {
query = query . Where ( "orders.created_by = ?" , req . CreatedBy )
}
2024-07-30 22:02:36 +07:00
if req . Status != "" {
query = query . Where ( "orders.status = ?" , req . Status )
}
2024-07-26 13:39:08 +07:00
2024-08-04 01:14:59 +07:00
if ! req . IsAdmin && ! req . IsCustomer {
2024-07-26 22:32:24 +07:00
query = query . Where ( "orders.partner_id = ?" , req . PartnerID )
2024-07-26 13:39:08 +07:00
}
2024-07-30 22:02:36 +07:00
if req . StartDate != "" && req . EndDate != "" {
// Assuming req.Date and req.EndDate are in string format "YYYY-MM-DD"
2024-08-15 22:17:28 +07:00
startDate := req . StartDate + " 00:00:00"
endDate := req . EndDate + " 23:59:59"
2024-07-30 22:02:36 +07:00
query = query . Where ( "orders.created_at BETWEEN ? AND ?" , startDate , endDate )
2024-08-28 06:14:03 +07:00
} else {
currentTime := time . Now ()
startOfMonth := time . Date ( currentTime . Year (), currentTime . Month (), 1 , 0 , 0 , 0 , 0 , time . Local )
2024-08-31 13:50:43 +07:00
endOfMonth := startOfMonth . AddDate ( 0 , 1 , 0 ). Add ( - time . Second )
2024-08-28 06:14:03 +07:00
query = query . Where ( "orders.created_at BETWEEN ? AND ?" , startOfMonth , endOfMonth )
2024-07-30 22:02:36 +07:00
}
if req . SiteID != nil {
query = query . Where ( "orders.site_id = ?" , req . SiteID )
}
2024-08-10 23:21:01 +07:00
if req . Source != "" {
query = query . Where ( "orders.source = ?" , req . Source )
}
2024-07-26 13:39:08 +07:00
query = query . Group ( "orders.id, users.name, sites.name, orders.created_at, orders.payment_type, orders.status" )
2024-07-26 11:37:22 +07:00
2024-07-30 22:02:36 +07:00
query = query . Order ( "orders.created_at DESC" )
2024-07-26 11:37:22 +07:00
if err := query . Count ( & total ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when count history orders" , zap . Error ( err ))
return nil , 0 , err
}
if req . Offset > 0 {
2024-08-27 19:31:22 +07:00
query = query . Offset ( req . Offset )
2024-07-26 11:37:22 +07:00
}
if req . Limit > 0 {
query = query . Limit ( req . Limit )
}
2024-08-15 22:17:28 +07:00
if err := query . Debug (). Scan ( & orders ). Error ; err != nil {
2024-07-26 11:37:22 +07:00
logger . ContextLogger ( ctx ). Error ( "error when get all history orders" , zap . Error ( err ))
return nil , 0 , err
}
for i , order := range orders {
if order . RawTickets != "" {
orders [ i ]. Tickets = strings . Split ( order . RawTickets , ", " )
}
}
return orders , int ( total ), nil
}
2024-07-27 16:47:33 +07:00
func ( r * OrderRepository ) CountSoldOfTicket ( ctx mycontext . Context , req entity . OrderSearch ) ( * entity . TicketSoldDB , error ) {
ticketCount := new ( entity . TicketSoldDB )
query := r . db . Table ( "orders" ).
Select ( "sum(items.qty) as count" ).
Joins ( "left join order_items items on orders.id = items.order_id" ).
2024-08-28 06:14:03 +07:00
Where ( "orders.status = ?" , "PAID" ).
Where ( "EXTRACT(MONTH FROM orders.created_at) = ?" , time . Now (). Month ())
2024-07-27 16:47:33 +07:00
if ! req . IsAdmin {
query = query . Where ( "orders.partner_id = ?" , req . PartnerID )
}
if err := query . Scan ( & ticketCount ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when get count ticket" , zap . Error ( err ))
return nil , err
}
return ticketCount , nil
}
func ( r * OrderRepository ) SumAmount ( ctx mycontext . Context , req entity . OrderSearch ) ( * entity . OrderDB , error ) {
amount := new ( entity . OrderDB )
query := r . db . Table ( "orders" ).
Select ( "sum(amount) as amount" ).
2024-08-28 06:14:03 +07:00
Where ( "status = ?" , "PAID" ).
Where ( "EXTRACT(MONTH FROM orders.created_at) = ?" , time . Now (). Month ())
2024-07-27 16:47:33 +07:00
2024-08-22 02:38:22 +07:00
if req . PaymentType == "CASH" {
query = query . Where ( "payment_type = ?" , req . PaymentType )
} else {
query = query . Where ( "payment_type != ?" , "CASH" )
}
2024-08-02 18:35:42 +07:00
if req . PartnerID != nil {
2024-07-27 16:47:33 +07:00
query = query . Where ( "orders.partner_id = ?" , req . PartnerID )
}
2024-08-02 18:35:42 +07:00
if req . SiteID != nil {
query = query . Where ( "orders.site_id = ?" , req . SiteID )
}
2024-07-27 16:47:33 +07:00
if err := query . Scan ( & amount ). Error ; err != nil {
logger . ContextLogger ( ctx ). Error ( "error when get cash amount" , zap . Error ( err ))
return nil , err
}
return amount , nil
}
2024-08-02 02:27:57 +07:00
func ( r * OrderRepository ) GetDailySalesMetrics ( ctx context . Context , req entity . OrderSearch ) ([] entity . ProductDailySales , error ) {
var sales [] entity . ProductDailySales
2024-08-02 14:41:41 +07:00
var dateTrunc , periodFilter string
now := time . Now ()
switch req . Period {
case "1d" :
dateTrunc = "hour"
periodFilter = now . Add ( - 24 * time . Hour ). Format ( "2006-01-02 15:04:05" )
case "7d" :
dateTrunc = "day"
periodFilter = now . Add ( - 7 * 24 * time . Hour ). Format ( "2006-01-02 15:04:05" )
case "1m" :
dateTrunc = "day"
periodFilter = now . AddDate ( 0 , - 1 , 0 ). Format ( "2006-01-02 15:04:05" )
case "1y" :
dateTrunc = "week"
periodFilter = now . AddDate ( - 1 , 0 , 0 ). Format ( "2006-01-02 15:04:05" )
default :
dateTrunc = "day"
periodFilter = now . AddDate ( 0 , - 1 , 0 ). Format ( "2006-01-02 15:04:05" ) // Default to last month
}
2024-08-02 02:27:57 +07:00
// Build the query with GORM
query := r . db . WithContext ( ctx ).
Table ( "orders o" ).
2024-08-02 15:16:28 +07:00
Select ( `DATE_TRUNC(?, o.created_at) AS day, s.id AS site_id, s.name AS site_name, o.payment_type, SUM(oi.qty * oi.price) AS total` , dateTrunc ).
2024-08-02 14:41:41 +07:00
Joins ( "JOIN order_items oi ON o.id = oi.order_id" ).
Joins ( "JOIN sites s ON o.site_id = s.id" ).
2024-08-02 02:27:57 +07:00
Where ( "o.status = ?" , "PAID" ).
2024-08-02 14:41:41 +07:00
Where ( "o.created_at >= ?" , periodFilter )
2024-08-02 02:27:57 +07:00
if req . PartnerID != nil {
query = query . Where ( "o.partner_id = ?" , * req . PartnerID )
}
2024-08-02 14:41:41 +07:00
2024-08-02 02:27:57 +07:00
if req . SiteID != nil {
query = query . Where ( "o.site_id = ?" , * req . SiteID )
}
2024-08-02 14:41:41 +07:00
query = query . Group ( "day, s.id, s.name, o.payment_type" ).
Order ( "day" )
2024-08-02 02:27:57 +07:00
if err := query . Find ( & sales ). Error ; err != nil {
return nil , err
}
return sales , nil
}
2024-08-02 15:16:28 +07:00
func ( r * OrderRepository ) GetPaymentTypeDistribution ( ctx context . Context , req entity . OrderSearch ) ([] entity . PaymentTypeDistribution , error ) {
var distribution [] entity . PaymentTypeDistribution
var periodFilter string
now := time . Now ()
switch req . Period {
case "1d" :
periodFilter = now . Add ( - 24 * time . Hour ). Format ( "2006-01-02 15:04:05" )
case "7d" :
periodFilter = now . Add ( - 7 * 24 * time . Hour ). Format ( "2006-01-02 15:04:05" )
case "1m" :
periodFilter = now . AddDate ( 0 , - 1 , 0 ). Format ( "2006-01-02 15:04:05" )
case "1y" :
periodFilter = now . AddDate ( - 1 , 0 , 0 ). Format ( "2006-01-02 15:04:05" )
default :
periodFilter = now . AddDate ( 0 , - 1 , 0 ). Format ( "2006-01-02 15:04:05" ) // Default to last month
}
query := r . db . WithContext ( ctx ).
Table ( "orders o" ).
Select ( "payment_type, COUNT(*) as count" ).
Where ( "status = ?" , "PAID" ).
Where ( "o.created_at >= ?" , periodFilter )
if req . PartnerID != nil {
query = query . Where ( "o.partner_id = ?" , * req . PartnerID )
}
if req . SiteID != nil {
query = query . Where ( "o.site_id = ?" , * req . SiteID )
}
query = query . Group ( "payment_type" )
if err := query . Scan ( & distribution ). Error ; err != nil {
return nil , err
}
return distribution , nil
}