feat: Add new stat stores and reporting functionalities for bets, branches, and wallets
- Introduced BetStatStore, BranchStatStore, and WalletStatStore interfaces for handling statistics. - Implemented repository methods for fetching and updating bet, branch, and wallet statistics. - Created reporting services for generating interval reports for bets, branches, companies, and wallets. - Enhanced CSV writing functionality to support dynamic struct to CSV conversion. - Added cron jobs for periodic updates of branch and wallet statistics. - Updated wallet handler to include transaction statistics in the response.
This commit is contained in:
parent
e5fdd33a52
commit
485cba3c9c
|
|
@ -113,8 +113,11 @@ func main() {
|
||||||
settingSvc := settings.NewService(repository.NewSettingStore(store))
|
settingSvc := settings.NewService(repository.NewSettingStore(store))
|
||||||
messengerSvc := messenger.NewService(settingSvc, cfg)
|
messengerSvc := messenger.NewService(settingSvc, cfg)
|
||||||
statSvc := stats.NewService(
|
statSvc := stats.NewService(
|
||||||
repository.NewCompanyStatStore(store),
|
repository.NewCompanyStatStore(store),
|
||||||
|
repository.NewBranchStatStore(store),
|
||||||
repository.NewEventStatStore(store),
|
repository.NewEventStatStore(store),
|
||||||
|
repository.NewBetStatStore(store),
|
||||||
|
repository.NewWalletStatStore(store),
|
||||||
)
|
)
|
||||||
|
|
||||||
authSvc := authentication.NewService(
|
authSvc := authentication.NewService(
|
||||||
|
|
@ -387,7 +390,7 @@ func main() {
|
||||||
aleaService,
|
aleaService,
|
||||||
// veliService,
|
// veliService,
|
||||||
recommendationSvc,
|
recommendationSvc,
|
||||||
resultSvc,
|
resultSvc,
|
||||||
statSvc,
|
statSvc,
|
||||||
cfg,
|
cfg,
|
||||||
domain.MongoDBLogger,
|
domain.MongoDBLogger,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,24 @@ CREATE TABLE IF NOT EXISTS wallets (
|
||||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
CONSTRAINT balance_positve CHECK (balance >= 0)
|
CONSTRAINT balance_positve CHECK (balance >= 0)
|
||||||
);
|
);
|
||||||
|
CREATE TABLE wallet_stats (
|
||||||
|
wallet_id BIGINT NOT NULL,
|
||||||
|
wallet_user_id BIGINT NOT NULL,
|
||||||
|
wallet_user_first_name TEXT NOT NULL,
|
||||||
|
wallet_user_last_name TEXT NOT NULL,
|
||||||
|
wallet_type TEXT NOT NULL,
|
||||||
|
interval_start TIMESTAMP NOT NULL,
|
||||||
|
number_of_transactions BIGINT NOT NULL,
|
||||||
|
total_transactions BIGINT NOT NULL,
|
||||||
|
number_of_deposits BIGINT NOT NULL,
|
||||||
|
total_deposits_amount BIGINT NOT NULL,
|
||||||
|
number_of_withdraws BIGINT NOT NULL,
|
||||||
|
total_withdraws_amount BIGINT NOT NULL,
|
||||||
|
number_of_transfers BIGINT NOT NULL,
|
||||||
|
total_transfers_amount BIGINT NOT NULL,
|
||||||
|
updated_at TIMESTAMP DEFAULT now(),
|
||||||
|
UNIQUE(wallet_id, interval_start)
|
||||||
|
);
|
||||||
CREATE TABLE refresh_tokens (
|
CREATE TABLE refresh_tokens (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
user_id BIGINT NOT NULL,
|
user_id BIGINT NOT NULL,
|
||||||
|
|
@ -298,6 +316,24 @@ CREATE TABLE IF NOT EXISTS branch_cashiers (
|
||||||
UNIQUE (user_id, branch_id)
|
UNIQUE (user_id, branch_id)
|
||||||
);
|
);
|
||||||
CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
CREATE TABLE IF NOT EXISTS branch_locations (key TEXT PRIMARY KEY, value TEXT NOT NULL);
|
||||||
|
CREATE TABLE branch_stats (
|
||||||
|
branch_id BIGINT NOT NULL,
|
||||||
|
branch_name TEXT NOT NULL,
|
||||||
|
company_id BIGINT NOT NULL,
|
||||||
|
company_name TEXT NOT NULL,
|
||||||
|
company_slug TEXT NOT NULL,
|
||||||
|
interval_start TIMESTAMP NOT NULL,
|
||||||
|
total_bets BIGINT NOT NULL,
|
||||||
|
total_stake BIGINT NOT NULL,
|
||||||
|
deducted_stake BIGINT NOT NULL,
|
||||||
|
total_cash_out BIGINT NOT NULL,
|
||||||
|
total_cash_backs BIGINT NOT NULL,
|
||||||
|
number_of_unsettled BIGINT NOT NULL,
|
||||||
|
total_unsettled_amount BIGINT NOT NULL,
|
||||||
|
total_cashiers BIGINT NOT NULL,
|
||||||
|
updated_at TIMESTAMP DEFAULT now(),
|
||||||
|
UNIQUE(branch_id, interval_start)
|
||||||
|
);
|
||||||
CREATE TABLE events (
|
CREATE TABLE events (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
source_event_id TEXT NOT NULL,
|
source_event_id TEXT NOT NULL,
|
||||||
|
|
@ -435,6 +471,8 @@ CREATE TABLE companies (
|
||||||
);
|
);
|
||||||
CREATE TABLE company_stats (
|
CREATE TABLE company_stats (
|
||||||
company_id BIGINT NOT NULL,
|
company_id BIGINT NOT NULL,
|
||||||
|
company_name TEXT NOT NULL,
|
||||||
|
company_slug TEXT NOT NULL,
|
||||||
interval_start TIMESTAMP NOT NULL,
|
interval_start TIMESTAMP NOT NULL,
|
||||||
total_bets BIGINT NOT NULL,
|
total_bets BIGINT NOT NULL,
|
||||||
total_stake BIGINT NOT NULL,
|
total_stake BIGINT NOT NULL,
|
||||||
|
|
@ -634,11 +672,27 @@ SELECT branches.*,
|
||||||
users.phone_number AS manager_phone_number,
|
users.phone_number AS manager_phone_number,
|
||||||
wallets.balance,
|
wallets.balance,
|
||||||
wallets.is_active AS wallet_is_active,
|
wallets.is_active AS wallet_is_active,
|
||||||
companies.name AS company_name
|
companies.name AS company_name,
|
||||||
|
COALESCE(bs.total_bets, 0) AS total_bets,
|
||||||
|
COALESCE(bs.total_stake, 0) AS total_stake,
|
||||||
|
COALESCE(bs.deducted_stake, 0) AS deducted_stake,
|
||||||
|
COALESCE(bs.total_cash_out, 0) AS total_cash_out,
|
||||||
|
COALESCE(bs.total_cash_backs, 0) AS total_cash_backs,
|
||||||
|
COALESCE(bs.number_of_unsettled, 0) AS number_of_unsettled,
|
||||||
|
COALESCE(bs.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||||
|
COALESCE(bs.total_cashiers, 0) AS total_cashiers,
|
||||||
|
bs.updated_at AS stats_updated_at
|
||||||
FROM branches
|
FROM branches
|
||||||
LEFT JOIN users ON branches.branch_manager_id = users.id
|
LEFT JOIN users ON branches.branch_manager_id = users.id
|
||||||
LEFT JOIN wallets ON wallets.id = branches.wallet_id
|
LEFT JOIN wallets ON wallets.id = branches.wallet_id
|
||||||
JOIN companies ON companies.id = branches.company_id;
|
JOIN companies ON companies.id = branches.company_id
|
||||||
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT *
|
||||||
|
FROM branch_stats s
|
||||||
|
WHERE s.branch_id = branches.id
|
||||||
|
ORDER BY s.interval_start DESC
|
||||||
|
LIMIT 1
|
||||||
|
) bs ON true;
|
||||||
CREATE TABLE IF NOT EXISTS supported_operations (
|
CREATE TABLE IF NOT EXISTS supported_operations (
|
||||||
id BIGSERIAL PRIMARY KEY,
|
id BIGSERIAL PRIMARY KEY,
|
||||||
name VARCHAR(255) NOT NULL,
|
name VARCHAR(255) NOT NULL,
|
||||||
|
|
@ -679,11 +733,27 @@ SELECT cw.id,
|
||||||
cw.created_at,
|
cw.created_at,
|
||||||
users.first_name,
|
users.first_name,
|
||||||
users.last_name,
|
users.last_name,
|
||||||
users.phone_number
|
users.phone_number,
|
||||||
|
COALESCE(cs.number_of_transactions, 0) AS number_of_transactions,
|
||||||
|
COALESCE(cs.total_transactions, 0) AS total_transactions,
|
||||||
|
COALESCE(cs.number_of_deposits, 0) AS number_of_deposits,
|
||||||
|
COALESCE(cs.total_deposits_amount, 0) AS total_deposits_amount,
|
||||||
|
COALESCE(cs.number_of_withdraws, 0) AS number_of_withdraws,
|
||||||
|
COALESCE(cs.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||||
|
COALESCE(cs.number_of_transfers, 0) AS number_of_transfers,
|
||||||
|
COALESCE(cs.total_transfers_amount, 0) AS total_transfers_amount,
|
||||||
|
cs.updated_at AS stats_updated_at
|
||||||
FROM customer_wallets cw
|
FROM customer_wallets cw
|
||||||
JOIN wallets rw ON cw.regular_wallet_id = rw.id
|
JOIN wallets rw ON cw.regular_wallet_id = rw.id
|
||||||
JOIN wallets sw ON cw.static_wallet_id = sw.id
|
JOIN wallets sw ON cw.static_wallet_id = sw.id
|
||||||
JOIN users ON users.id = cw.customer_id;
|
JOIN users ON users.id = cw.customer_id
|
||||||
|
LEFT JOIN LATERAL (
|
||||||
|
SELECT *
|
||||||
|
FROM wallet_stats s
|
||||||
|
WHERE s.wallet_id = cw.regular_wallet
|
||||||
|
ORDER BY s.interval_start DESC
|
||||||
|
LIMIT 1
|
||||||
|
) cs ON true;
|
||||||
CREATE VIEW wallet_transfer_details AS
|
CREATE VIEW wallet_transfer_details AS
|
||||||
SELECT wt.*,
|
SELECT wt.*,
|
||||||
users.first_name,
|
users.first_name,
|
||||||
|
|
@ -823,7 +893,8 @@ SELECT r.*,
|
||||||
c.name AS company_name,
|
c.name AS company_name,
|
||||||
c.slug AS company_slug,
|
c.slug AS company_slug,
|
||||||
u.first_name AS requester_first_name,
|
u.first_name AS requester_first_name,
|
||||||
u.last_name AS requester_last_name
|
u.last_name AS requester_last_name,
|
||||||
|
u.role AS requester_role
|
||||||
FROM report_requests r
|
FROM report_requests r
|
||||||
LEFT JOIN companies c ON c.id = r.company_id
|
LEFT JOIN companies c ON c.id = r.company_id
|
||||||
LEFT JOIN users u ON u.id = r.requested_by;
|
LEFT JOIN users u ON u.id = r.requested_by;
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,48 @@
|
||||||
|
-- name: GetBetStatsByInterval :many
|
||||||
|
SELECT DATE_TRUNC(sqlc.narg('interval'), created_at)::timestamp AS date,
|
||||||
|
COUNT(*) as total_bets,
|
||||||
|
SUM(amount) AS total_stake,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 0 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as active_bets,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 1 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as total_wins,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 2 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as total_losses,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 1 THEN amount * total_odds
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as win_balance,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE status = 5
|
||||||
|
) AS number_of_unsettled,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 5 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS total_unsettled_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE is_shop_bet = TRUE
|
||||||
|
) AS total_shop_bets
|
||||||
|
FROM bets
|
||||||
|
WHERE (
|
||||||
|
bets.company_id = sqlc.narg('company_id')
|
||||||
|
OR sqlc.narg('company_id') IS NULL
|
||||||
|
);
|
||||||
-- name: GetBetSummary :one
|
-- name: GetBetSummary :one
|
||||||
SELECT SUM(amount) as total_stakes,
|
SELECT SUM(amount) as total_stakes,
|
||||||
COUNT(*) as total_bets,
|
COUNT(*) as total_bets,
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,128 @@
|
||||||
-- name: GetBranchStats :many
|
-- name: UpdateBranchStats :exec
|
||||||
SELECT b.branch_id,
|
WITH -- Aggregate bet data per branch
|
||||||
|
bet_stats AS (
|
||||||
|
SELECT branch_id,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(amount), 0) AS total_stake,
|
||||||
|
COALESCE(
|
||||||
|
SUM(amount) * MAX(companies.deducted_percentage),
|
||||||
|
0
|
||||||
|
) AS deducted_stake,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN cashed_out THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_cash_out,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 3 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_cash_backs,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE status = 5
|
||||||
|
) AS number_of_unsettled,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 5 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_unsettled_amount
|
||||||
|
FROM shop_bet_detail
|
||||||
|
LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id
|
||||||
|
GROUP BY branch_id
|
||||||
|
),
|
||||||
|
cashier_stats AS (
|
||||||
|
SELECT branch_id,
|
||||||
|
COUNT(*) AS total_cashiers
|
||||||
|
FROM branch_cashiers
|
||||||
|
GROUP BY branch_id
|
||||||
|
)
|
||||||
|
INSERT INTO branch_stats (
|
||||||
|
branch_id,
|
||||||
|
branch_name,
|
||||||
|
company_id,
|
||||||
|
company_name,
|
||||||
|
company_slug,
|
||||||
|
interval_start,
|
||||||
|
total_bets,
|
||||||
|
total_stake,
|
||||||
|
deducted_stake,
|
||||||
|
total_cash_out,
|
||||||
|
total_cash_backs,
|
||||||
|
number_of_unsettled,
|
||||||
|
total_unsettled_amount,
|
||||||
|
total_cashiers,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
SELECT br.id AS branch_id,
|
||||||
br.name AS branch_name,
|
br.name AS branch_name,
|
||||||
br.company_id,
|
c.id AS company_id,
|
||||||
COUNT(*) AS total_bets,
|
c.name AS company_name,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
c.slug AS company_slug,
|
||||||
COALESCE(
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
SUM(
|
COALESCE(b.total_bets, 0) AS total_bets,
|
||||||
CASE
|
COALESCE(b.total_stake, 0) AS total_stake,
|
||||||
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
COALESCE(b.deducted_stake, 0) AS deducted_stake,
|
||||||
ELSE 0
|
COALESCE(b.total_cash_out, 0) AS total_cash_out,
|
||||||
END
|
COALESCE(b.total_cash_backs, 0) AS total_cash_backs,
|
||||||
),
|
COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled,
|
||||||
0
|
COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||||
) AS total_cash_out,
|
COALESCE(bc.total_cashiers, 0) AS total_cashiers,
|
||||||
COALESCE(
|
NOW() AS updated_at
|
||||||
SUM(
|
FROM branches br
|
||||||
CASE
|
LEFT JOIN companies c ON c.id = br.company_id
|
||||||
WHEN b.status = 5 THEN b.amount
|
LEFT JOIN bet_stats bs ON b.branch_id = br.id
|
||||||
ELSE 0
|
LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO
|
||||||
END
|
UPDATE
|
||||||
),
|
SET total_bets = EXCLUDED.total_bets,
|
||||||
0
|
total_stake = EXCLUDED.total_stake,
|
||||||
) AS total_cash_backs
|
deducted_stake = EXCLUDED.deducted_stake,
|
||||||
FROM shop_bet_detail b
|
total_cash_out = EXCLUDED.total_cash_out,
|
||||||
JOIN branches br ON b.branch_id = br.id
|
total_cash_backs = EXCLUDED.total_cash_backs,
|
||||||
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
number_of_unsettled = EXCLUDED.number_of_unsettled,
|
||||||
WHERE b.created_at BETWEEN sqlc.arg('from') AND sqlc.arg('to')
|
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||||
GROUP BY b.branch_id,
|
total_cashiers = EXCLUDED.total_cashiers,
|
||||||
br.name,
|
updated_at = EXCLUDED.updated_at;
|
||||||
br.company_id;
|
-- name: GetBranchStatsByID :many
|
||||||
|
SELECt *
|
||||||
|
FROM branch_stats
|
||||||
|
WHERE branch_id = $1
|
||||||
|
ORDER BY interval_start DESC;
|
||||||
|
-- name: GetBranchStats :many
|
||||||
|
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||||
|
branch_stats.branch_id,
|
||||||
|
branch_stats.branch_name,
|
||||||
|
branch_stats.company_id,
|
||||||
|
branch_stats.company_name,
|
||||||
|
branch_stats.company_slug,
|
||||||
|
branch_stats.total_bets,
|
||||||
|
branch_stats.total_stake,
|
||||||
|
branch_stats.deducted_stake,
|
||||||
|
branch_stats.total_cash_out,
|
||||||
|
branch_stats.total_cash_backs,
|
||||||
|
branch_stats.number_of_unsettled,
|
||||||
|
branch_stats.total_unsettled_amount,
|
||||||
|
branch_stats.total_cashiers,
|
||||||
|
branch_stats.updated_at
|
||||||
|
FROM branch_stats
|
||||||
|
WHERE (
|
||||||
|
branch_stats.branch_id = sqlc.narg('branch_id')
|
||||||
|
OR sqlc.narg('branch_id') IS NULL
|
||||||
|
)
|
||||||
|
AND (
|
||||||
|
branch_stats.company_id = sqlc.narg('company_id')
|
||||||
|
OR sqlc.narg('company_id') IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY interval_start
|
||||||
|
ORDER BY interval_start DESC;
|
||||||
|
|
@ -72,6 +72,8 @@ branch_stats AS (
|
||||||
) -- Final combined aggregation
|
) -- Final combined aggregation
|
||||||
INSERT INTO company_stats (
|
INSERT INTO company_stats (
|
||||||
company_id,
|
company_id,
|
||||||
|
company_name,
|
||||||
|
company_slug,
|
||||||
interval_start,
|
interval_start,
|
||||||
total_bets,
|
total_bets,
|
||||||
total_stake,
|
total_stake,
|
||||||
|
|
@ -89,6 +91,8 @@ INSERT INTO company_stats (
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
SELECT c.id AS company_id,
|
SELECT c.id AS company_id,
|
||||||
|
c.name AS company_name,
|
||||||
|
c.slug AS company_slug,
|
||||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
COALESCE(b.total_bets, 0) AS total_bets,
|
COALESCE(b.total_bets, 0) AS total_bets,
|
||||||
COALESCE(b.total_stake, 0) AS total_stake,
|
COALESCE(b.total_stake, 0) AS total_stake,
|
||||||
|
|
@ -118,7 +122,7 @@ SET total_bets = EXCLUDED.total_bets,
|
||||||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||||
total_admins = EXCLUDED.total_admins,
|
total_admins = EXCLUDED.total_admins,
|
||||||
total_managers = EXCLUDED.total_managers,
|
total_managers = EXCLUDED.total_managers,
|
||||||
total_cashiers = EXCLUDED.total_cashiers,
|
SETtotal_cashiers = EXCLUDED.total_cashiers,
|
||||||
total_customers = EXCLUDED.total_customers,
|
total_customers = EXCLUDED.total_customers,
|
||||||
total_approvers = EXCLUDED.total_approvers,
|
total_approvers = EXCLUDED.total_approvers,
|
||||||
total_branches = EXCLUDED.total_branches,
|
total_branches = EXCLUDED.total_branches,
|
||||||
|
|
@ -130,7 +134,23 @@ WHERE company_id = $1
|
||||||
ORDER BY interval_start DESC;
|
ORDER BY interval_start DESC;
|
||||||
-- name: GetCompanyStats :many
|
-- name: GetCompanyStats :many
|
||||||
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||||
company_stats.*
|
company_stats.company_id,
|
||||||
|
company_stats.company_name,
|
||||||
|
company_stats.company_slug,
|
||||||
|
company_stats.total_bets,
|
||||||
|
company_stats.total_stake,
|
||||||
|
company_stats.deducted_stake,
|
||||||
|
company_stats.total_cash_out,
|
||||||
|
company_stats.total_cash_backs,
|
||||||
|
company_stats.number_of_unsettled,
|
||||||
|
company_stats.total_unsettled_amount,
|
||||||
|
company_stats.total_admins,
|
||||||
|
company_stats.total_managers,
|
||||||
|
company_stats.total_cashiers,
|
||||||
|
company_stats.total_customers,
|
||||||
|
company_stats.total_approvers,
|
||||||
|
company_stats.total_branches,
|
||||||
|
company_stats.updated_at
|
||||||
FROM company_stats
|
FROM company_stats
|
||||||
WHERE (
|
WHERE (
|
||||||
company_stats.company_id = sqlc.narg('company_id')
|
company_stats.company_id = sqlc.narg('company_id')
|
||||||
|
|
|
||||||
130
db/query/wallet_stats.sql
Normal file
130
db/query/wallet_stats.sql
Normal file
|
|
@ -0,0 +1,130 @@
|
||||||
|
-- name: UpdateWalletStats :exec
|
||||||
|
WITH all_transfers AS (
|
||||||
|
SELECT sender_wallet_id AS wallet_id,
|
||||||
|
amount,
|
||||||
|
type
|
||||||
|
FROM wallet_transfer
|
||||||
|
WHERE sender_wallet_id IS NOT NULL
|
||||||
|
UNION ALL
|
||||||
|
SELECT receiver_wallet_id AS wallet_id,
|
||||||
|
amount,
|
||||||
|
type
|
||||||
|
FROM wallet_transfer
|
||||||
|
WHERE receiver_wallet_id IS NOT NULL
|
||||||
|
),
|
||||||
|
transfer_stats AS (
|
||||||
|
SELECT wallet_id,
|
||||||
|
COUNT(*) AS number_of_transactions,
|
||||||
|
COALESCE(SUM(amount), 0) AS total_transactions,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'deposit'
|
||||||
|
) AS number_of_deposits,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'deposit' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_deposits_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'withdraw'
|
||||||
|
) AS number_of_withdraws,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'withdraw' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_withdraws_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'wallet'
|
||||||
|
) AS number_of_transfers,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'wallet' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_transfers_amount
|
||||||
|
FROM all_transfers
|
||||||
|
GROUP BY wallet_id
|
||||||
|
)
|
||||||
|
INSERT INTO wallet_stats(
|
||||||
|
wallet_id,
|
||||||
|
wallet_user_id,
|
||||||
|
wallet_user_first_name,
|
||||||
|
wallet_user_last_name,
|
||||||
|
wallet_type,
|
||||||
|
interval_start,
|
||||||
|
number_of_transactions,
|
||||||
|
total_transactions,
|
||||||
|
number_of_deposits,
|
||||||
|
total_deposits_amount,
|
||||||
|
number_of_withdraws,
|
||||||
|
total_withdraws_amount,
|
||||||
|
number_of_transfers,
|
||||||
|
total_transfers_amount,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
SELECT w.id AS wallet_id,
|
||||||
|
w.user_id AS wallet_user_id,
|
||||||
|
u.first_name AS wallet_user_first_name,
|
||||||
|
u.last_name AS wallet_user_last_name,
|
||||||
|
w.type AS wallet_type,
|
||||||
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
|
COALESCE(ts.number_of_transactions, 0) AS number_of_transactions,
|
||||||
|
COALESCE(ts.total_transactions, 0) AS total_transactions,
|
||||||
|
COALESCE(ts.number_of_deposits, 0) AS number_of_deposits,
|
||||||
|
COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount,
|
||||||
|
COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws,
|
||||||
|
COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||||
|
COALESCE(ts.number_of_transfers, 0) AS number_of_transfers,
|
||||||
|
COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount,
|
||||||
|
NOW() AS updated_at
|
||||||
|
FROM wallets w
|
||||||
|
LEFT JOIN users u ON u.id = w.user_id
|
||||||
|
LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO
|
||||||
|
UPDATE
|
||||||
|
SET number_of_transactions = EXCLUDED.number_of_transactions,
|
||||||
|
total_transactions = EXCLUDED.total_transactions,
|
||||||
|
number_of_deposits = EXCLUDED.number_of_deposits,
|
||||||
|
total_deposits_amount = EXCLUDED.total_deposits_amount,
|
||||||
|
number_of_withdraws = EXCLUDED.number_of_withdraws,
|
||||||
|
total_withdraws_amount = EXCLUDED.total_withdraws_amount,
|
||||||
|
number_of_transfers = EXCLUDED.number_of_transfers,
|
||||||
|
total_transfers_amount = EXCLUDED.total_transfers_amount,
|
||||||
|
updated_at = EXCLUDED.updated_at;
|
||||||
|
-- name: GetWalletStatsByID :many
|
||||||
|
SELECT *
|
||||||
|
FROM wallet_stats
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
ORDER BY interval_start DESC;
|
||||||
|
-- name: GetWalletStats :many
|
||||||
|
SELECT DATE_TRUNC(sqlc.narg('interval'), interval_start)::timestamp AS interval_start,
|
||||||
|
wallet_stats.wallet_id,
|
||||||
|
wallet_stats.wallet_user_id,
|
||||||
|
wallet_stats.wallet_user_first_name,
|
||||||
|
wallet_stats.wallet_user_last_name,
|
||||||
|
wallet_stats.wallet_type,
|
||||||
|
wallet_stats.number_of_transactions,
|
||||||
|
wallet_stats.total_transactions,
|
||||||
|
wallet_stats.number_of_deposits,
|
||||||
|
wallet_stats.total_deposits_amount,
|
||||||
|
wallet_stats.number_of_withdraws,
|
||||||
|
wallet_stats.total_withdraws_amount,
|
||||||
|
wallet_stats.number_of_transfers,
|
||||||
|
wallet_stats.total_transfers_amount,
|
||||||
|
wallet_stats.updated_at
|
||||||
|
FROM wallet_stats
|
||||||
|
WHERE (
|
||||||
|
wallet_stats.wallet_user_id = sqlc.narg('user_id')
|
||||||
|
OR sqlc.narg('user_id') IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY interval_start
|
||||||
|
ORDER BY interval_start DESC;
|
||||||
|
|
@ -116,6 +116,102 @@ func (q *Queries) GetBetStats(ctx context.Context, arg GetBetStatsParams) ([]Get
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetBetStatsByInterval = `-- name: GetBetStatsByInterval :many
|
||||||
|
SELECT DATE_TRUNC($1, created_at)::timestamp AS date,
|
||||||
|
COUNT(*) as total_bets,
|
||||||
|
SUM(amount) AS total_stake,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 0 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as active_bets,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 1 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as total_wins,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 2 THEN 1
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as total_losses,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 1 THEN amount * total_odds
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) as win_balance,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE status = 5
|
||||||
|
) AS number_of_unsettled,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 5 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS total_unsettled_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE is_shop_bet = TRUE
|
||||||
|
) AS total_shop_bets
|
||||||
|
FROM bets
|
||||||
|
WHERE (
|
||||||
|
bets.company_id = $2
|
||||||
|
OR $2 IS NULL
|
||||||
|
)
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetBetStatsByIntervalParams struct {
|
||||||
|
Interval pgtype.Text `json:"interval"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetBetStatsByIntervalRow struct {
|
||||||
|
Date pgtype.Timestamp `json:"date"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
ActiveBets int64 `json:"active_bets"`
|
||||||
|
TotalWins int64 `json:"total_wins"`
|
||||||
|
TotalLosses int64 `json:"total_losses"`
|
||||||
|
WinBalance int64 `json:"win_balance"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||||
|
TotalShopBets int64 `json:"total_shop_bets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetBetStatsByInterval(ctx context.Context, arg GetBetStatsByIntervalParams) ([]GetBetStatsByIntervalRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetBetStatsByInterval, arg.Interval, arg.CompanyID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetBetStatsByIntervalRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetBetStatsByIntervalRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.Date,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.ActiveBets,
|
||||||
|
&i.TotalWins,
|
||||||
|
&i.TotalLosses,
|
||||||
|
&i.WinBalance,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalShopBets,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
const GetBetSummary = `-- name: GetBetSummary :one
|
const GetBetSummary = `-- name: GetBetSummary :one
|
||||||
SELECT SUM(amount) as total_stakes,
|
SELECT SUM(amount) as total_stakes,
|
||||||
COUNT(*) as total_bets,
|
COUNT(*) as total_bets,
|
||||||
|
|
|
||||||
|
|
@ -159,7 +159,7 @@ func (q *Queries) DeleteBranchOperation(ctx context.Context, arg DeleteBranchOpe
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetAllBranches = `-- name: GetAllBranches :many
|
const GetAllBranches = `-- name: GetAllBranches :many
|
||||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||||
FROM branch_details
|
FROM branch_details
|
||||||
WHERE (
|
WHERE (
|
||||||
company_id = $1
|
company_id = $1
|
||||||
|
|
@ -230,6 +230,15 @@ func (q *Queries) GetAllBranches(ctx context.Context, arg GetAllBranchesParams)
|
||||||
&i.Balance,
|
&i.Balance,
|
||||||
&i.WalletIsActive,
|
&i.WalletIsActive,
|
||||||
&i.CompanyName,
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -293,7 +302,7 @@ func (q *Queries) GetBranchByCashier(ctx context.Context, userID int64) (Branch,
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many
|
const GetBranchByCompanyID = `-- name: GetBranchByCompanyID :many
|
||||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||||
FROM branch_details
|
FROM branch_details
|
||||||
WHERE company_id = $1
|
WHERE company_id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -324,6 +333,15 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
|
||||||
&i.Balance,
|
&i.Balance,
|
||||||
&i.WalletIsActive,
|
&i.WalletIsActive,
|
||||||
&i.CompanyName,
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -336,7 +354,7 @@ func (q *Queries) GetBranchByCompanyID(ctx context.Context, companyID int64) ([]
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetBranchByID = `-- name: GetBranchByID :one
|
const GetBranchByID = `-- name: GetBranchByID :one
|
||||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||||
FROM branch_details
|
FROM branch_details
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -361,12 +379,21 @@ func (q *Queries) GetBranchByID(ctx context.Context, id int64) (BranchDetail, er
|
||||||
&i.Balance,
|
&i.Balance,
|
||||||
&i.WalletIsActive,
|
&i.WalletIsActive,
|
||||||
&i.CompanyName,
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetBranchByManagerID = `-- name: GetBranchByManagerID :many
|
const GetBranchByManagerID = `-- name: GetBranchByManagerID :many
|
||||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||||
FROM branch_details
|
FROM branch_details
|
||||||
WHERE branch_manager_id = $1
|
WHERE branch_manager_id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -397,6 +424,15 @@ func (q *Queries) GetBranchByManagerID(ctx context.Context, branchManagerID int6
|
||||||
&i.Balance,
|
&i.Balance,
|
||||||
&i.WalletIsActive,
|
&i.WalletIsActive,
|
||||||
&i.CompanyName,
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -456,7 +492,7 @@ func (q *Queries) GetBranchOperations(ctx context.Context, branchID int64) ([]Ge
|
||||||
}
|
}
|
||||||
|
|
||||||
const SearchBranchByName = `-- name: SearchBranchByName :many
|
const SearchBranchByName = `-- name: SearchBranchByName :many
|
||||||
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name
|
SELECT id, name, location, profit_percent, is_active, wallet_id, branch_manager_id, company_id, is_self_owned, created_at, updated_at, manager_name, manager_phone_number, balance, wallet_is_active, company_name, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, stats_updated_at
|
||||||
FROM branch_details
|
FROM branch_details
|
||||||
WHERE name ILIKE '%' || $1 || '%'
|
WHERE name ILIKE '%' || $1 || '%'
|
||||||
AND (
|
AND (
|
||||||
|
|
@ -496,6 +532,15 @@ func (q *Queries) SearchBranchByName(ctx context.Context, arg SearchBranchByName
|
||||||
&i.Balance,
|
&i.Balance,
|
||||||
&i.WalletIsActive,
|
&i.WalletIsActive,
|
||||||
&i.CompanyName,
|
&i.CompanyName,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,55 +12,60 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const GetBranchStats = `-- name: GetBranchStats :many
|
const GetBranchStats = `-- name: GetBranchStats :many
|
||||||
SELECT b.branch_id,
|
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||||
br.name AS branch_name,
|
branch_stats.branch_id,
|
||||||
br.company_id,
|
branch_stats.branch_name,
|
||||||
COUNT(*) AS total_bets,
|
branch_stats.company_id,
|
||||||
COALESCE(SUM(b.amount), 0) AS total_cash_made,
|
branch_stats.company_name,
|
||||||
COALESCE(
|
branch_stats.company_slug,
|
||||||
SUM(
|
branch_stats.total_bets,
|
||||||
CASE
|
branch_stats.total_stake,
|
||||||
WHEN sb.cashed_out THEN b.amount -- use cashed_out from shop_bets
|
branch_stats.deducted_stake,
|
||||||
ELSE 0
|
branch_stats.total_cash_out,
|
||||||
END
|
branch_stats.total_cash_backs,
|
||||||
),
|
branch_stats.number_of_unsettled,
|
||||||
0
|
branch_stats.total_unsettled_amount,
|
||||||
) AS total_cash_out,
|
branch_stats.total_cashiers,
|
||||||
COALESCE(
|
branch_stats.updated_at
|
||||||
SUM(
|
FROM branch_stats
|
||||||
CASE
|
WHERE (
|
||||||
WHEN b.status = 5 THEN b.amount
|
branch_stats.branch_id = $2
|
||||||
ELSE 0
|
OR $2 IS NULL
|
||||||
END
|
)
|
||||||
),
|
AND (
|
||||||
0
|
branch_stats.company_id = $3
|
||||||
) AS total_cash_backs
|
OR $3 IS NULL
|
||||||
FROM shop_bet_detail b
|
)
|
||||||
JOIN branches br ON b.branch_id = br.id
|
GROUP BY interval_start
|
||||||
JOIN shop_bets sb ON sb.id = b.id -- join to get cashed_out
|
ORDER BY interval_start DESC
|
||||||
WHERE b.created_at BETWEEN $1 AND $2
|
|
||||||
GROUP BY b.branch_id,
|
|
||||||
br.name,
|
|
||||||
br.company_id
|
|
||||||
`
|
`
|
||||||
|
|
||||||
type GetBranchStatsParams struct {
|
type GetBranchStatsParams struct {
|
||||||
From pgtype.Timestamp `json:"from"`
|
Interval pgtype.Text `json:"interval"`
|
||||||
To pgtype.Timestamp `json:"to"`
|
BranchID pgtype.Int8 `json:"branch_id"`
|
||||||
|
CompanyID pgtype.Int8 `json:"company_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type GetBranchStatsRow struct {
|
type GetBranchStatsRow struct {
|
||||||
BranchID int64 `json:"branch_id"`
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
BranchName string `json:"branch_name"`
|
BranchID int64 `json:"branch_id"`
|
||||||
CompanyID int64 `json:"company_id"`
|
BranchName string `json:"branch_name"`
|
||||||
TotalBets int64 `json:"total_bets"`
|
CompanyID int64 `json:"company_id"`
|
||||||
TotalCashMade interface{} `json:"total_cash_made"`
|
CompanyName string `json:"company_name"`
|
||||||
TotalCashOut interface{} `json:"total_cash_out"`
|
CompanySlug string `json:"company_slug"`
|
||||||
TotalCashBacks interface{} `json:"total_cash_backs"`
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
DeductedStake int64 `json:"deducted_stake"`
|
||||||
|
TotalCashOut int64 `json:"total_cash_out"`
|
||||||
|
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||||
|
TotalCashiers int64 `json:"total_cashiers"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams) ([]GetBranchStatsRow, error) {
|
func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams) ([]GetBranchStatsRow, error) {
|
||||||
rows, err := q.db.Query(ctx, GetBranchStats, arg.From, arg.To)
|
rows, err := q.db.Query(ctx, GetBranchStats, arg.Interval, arg.BranchID, arg.CompanyID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -69,13 +74,21 @@ func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams)
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var i GetBranchStatsRow
|
var i GetBranchStatsRow
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
|
&i.IntervalStart,
|
||||||
&i.BranchID,
|
&i.BranchID,
|
||||||
&i.BranchName,
|
&i.BranchName,
|
||||||
&i.CompanyID,
|
&i.CompanyID,
|
||||||
|
&i.CompanyName,
|
||||||
|
&i.CompanySlug,
|
||||||
&i.TotalBets,
|
&i.TotalBets,
|
||||||
&i.TotalCashMade,
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
&i.TotalCashOut,
|
&i.TotalCashOut,
|
||||||
&i.TotalCashBacks,
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.UpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -86,3 +99,149 @@ func (q *Queries) GetBranchStats(ctx context.Context, arg GetBranchStatsParams)
|
||||||
}
|
}
|
||||||
return items, nil
|
return items, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GetBranchStatsByID = `-- name: GetBranchStatsByID :many
|
||||||
|
SELECt branch_id, branch_name, company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_cashiers, updated_at
|
||||||
|
FROM branch_stats
|
||||||
|
WHERE branch_id = $1
|
||||||
|
ORDER BY interval_start DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetBranchStatsByID(ctx context.Context, branchID int64) ([]BranchStat, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetBranchStatsByID, branchID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []BranchStat
|
||||||
|
for rows.Next() {
|
||||||
|
var i BranchStat
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.BranchID,
|
||||||
|
&i.BranchName,
|
||||||
|
&i.CompanyID,
|
||||||
|
&i.CompanyName,
|
||||||
|
&i.CompanySlug,
|
||||||
|
&i.IntervalStart,
|
||||||
|
&i.TotalBets,
|
||||||
|
&i.TotalStake,
|
||||||
|
&i.DeductedStake,
|
||||||
|
&i.TotalCashOut,
|
||||||
|
&i.TotalCashBacks,
|
||||||
|
&i.NumberOfUnsettled,
|
||||||
|
&i.TotalUnsettledAmount,
|
||||||
|
&i.TotalCashiers,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateBranchStats = `-- name: UpdateBranchStats :exec
|
||||||
|
WITH -- Aggregate bet data per branch
|
||||||
|
bet_stats AS (
|
||||||
|
SELECT branch_id,
|
||||||
|
COUNT(*) AS total_bets,
|
||||||
|
COALESCE(SUM(amount), 0) AS total_stake,
|
||||||
|
COALESCE(
|
||||||
|
SUM(amount) * MAX(companies.deducted_percentage),
|
||||||
|
0
|
||||||
|
) AS deducted_stake,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN cashed_out THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_cash_out,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 3 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_cash_backs,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE status = 5
|
||||||
|
) AS number_of_unsettled,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN status = 5 THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_unsettled_amount
|
||||||
|
FROM shop_bet_detail
|
||||||
|
LEFT JOIN branches ON branches.id = shop_bet_detail.branch_id
|
||||||
|
GROUP BY branch_id
|
||||||
|
),
|
||||||
|
cashier_stats AS (
|
||||||
|
SELECT branch_id,
|
||||||
|
COUNT(*) AS total_cashiers
|
||||||
|
FROM branch_cashiers
|
||||||
|
GROUP BY branch_id
|
||||||
|
)
|
||||||
|
INSERT INTO branch_stats (
|
||||||
|
branch_id,
|
||||||
|
branch_name,
|
||||||
|
company_id,
|
||||||
|
company_name,
|
||||||
|
company_slug,
|
||||||
|
interval_start,
|
||||||
|
total_bets,
|
||||||
|
total_stake,
|
||||||
|
deducted_stake,
|
||||||
|
total_cash_out,
|
||||||
|
total_cash_backs,
|
||||||
|
number_of_unsettled,
|
||||||
|
total_unsettled_amount,
|
||||||
|
total_cashiers,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
SELECT br.id AS branch_id,
|
||||||
|
br.name AS branch_name,
|
||||||
|
c.id AS company_id,
|
||||||
|
c.name AS company_name,
|
||||||
|
c.slug AS company_slug,
|
||||||
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
|
COALESCE(b.total_bets, 0) AS total_bets,
|
||||||
|
COALESCE(b.total_stake, 0) AS total_stake,
|
||||||
|
COALESCE(b.deducted_stake, 0) AS deducted_stake,
|
||||||
|
COALESCE(b.total_cash_out, 0) AS total_cash_out,
|
||||||
|
COALESCE(b.total_cash_backs, 0) AS total_cash_backs,
|
||||||
|
COALESCE(b.number_of_unsettled, 0) AS number_of_unsettled,
|
||||||
|
COALESCE(b.total_unsettled_amount, 0) AS total_unsettled_amount,
|
||||||
|
COALESCE(bc.total_cashiers, 0) AS total_cashiers,
|
||||||
|
NOW() AS updated_at
|
||||||
|
FROM branches br
|
||||||
|
LEFT JOIN companies c ON c.id = br.company_id
|
||||||
|
LEFT JOIN bet_stats bs ON b.branch_id = br.id
|
||||||
|
LEFT JOIN cashier_stats bc ON bc.branch_id = br.id ON CONFLICT (branch_id, interval_start) DO
|
||||||
|
UPDATE
|
||||||
|
SET total_bets = EXCLUDED.total_bets,
|
||||||
|
total_stake = EXCLUDED.total_stake,
|
||||||
|
deducted_stake = EXCLUDED.deducted_stake,
|
||||||
|
total_cash_out = EXCLUDED.total_cash_out,
|
||||||
|
total_cash_backs = EXCLUDED.total_cash_backs,
|
||||||
|
number_of_unsettled = EXCLUDED.number_of_unsettled,
|
||||||
|
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||||
|
total_cashiers = EXCLUDED.total_cashiers,
|
||||||
|
updated_at = EXCLUDED.updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) UpdateBranchStats(ctx context.Context) error {
|
||||||
|
_, err := q.db.Exec(ctx, UpdateBranchStats)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,23 @@ import (
|
||||||
|
|
||||||
const GetCompanyStats = `-- name: GetCompanyStats :many
|
const GetCompanyStats = `-- name: GetCompanyStats :many
|
||||||
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||||
company_stats.company_id, company_stats.interval_start, company_stats.total_bets, company_stats.total_stake, company_stats.deducted_stake, company_stats.total_cash_out, company_stats.total_cash_backs, company_stats.number_of_unsettled, company_stats.total_unsettled_amount, company_stats.total_admins, company_stats.total_managers, company_stats.total_cashiers, company_stats.total_customers, company_stats.total_approvers, company_stats.total_branches, company_stats.updated_at
|
company_stats.company_id,
|
||||||
|
company_stats.company_name,
|
||||||
|
company_stats.company_slug,
|
||||||
|
company_stats.total_bets,
|
||||||
|
company_stats.total_stake,
|
||||||
|
company_stats.deducted_stake,
|
||||||
|
company_stats.total_cash_out,
|
||||||
|
company_stats.total_cash_backs,
|
||||||
|
company_stats.number_of_unsettled,
|
||||||
|
company_stats.total_unsettled_amount,
|
||||||
|
company_stats.total_admins,
|
||||||
|
company_stats.total_managers,
|
||||||
|
company_stats.total_cashiers,
|
||||||
|
company_stats.total_customers,
|
||||||
|
company_stats.total_approvers,
|
||||||
|
company_stats.total_branches,
|
||||||
|
company_stats.updated_at
|
||||||
FROM company_stats
|
FROM company_stats
|
||||||
WHERE (
|
WHERE (
|
||||||
company_stats.company_id = $2
|
company_stats.company_id = $2
|
||||||
|
|
@ -31,7 +47,8 @@ type GetCompanyStatsParams struct {
|
||||||
type GetCompanyStatsRow struct {
|
type GetCompanyStatsRow struct {
|
||||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
CompanyID int64 `json:"company_id"`
|
CompanyID int64 `json:"company_id"`
|
||||||
IntervalStart_2 pgtype.Timestamp `json:"interval_start_2"`
|
CompanyName string `json:"company_name"`
|
||||||
|
CompanySlug string `json:"company_slug"`
|
||||||
TotalBets int64 `json:"total_bets"`
|
TotalBets int64 `json:"total_bets"`
|
||||||
TotalStake int64 `json:"total_stake"`
|
TotalStake int64 `json:"total_stake"`
|
||||||
DeductedStake int64 `json:"deducted_stake"`
|
DeductedStake int64 `json:"deducted_stake"`
|
||||||
|
|
@ -60,7 +77,8 @@ func (q *Queries) GetCompanyStats(ctx context.Context, arg GetCompanyStatsParams
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.IntervalStart,
|
&i.IntervalStart,
|
||||||
&i.CompanyID,
|
&i.CompanyID,
|
||||||
&i.IntervalStart_2,
|
&i.CompanyName,
|
||||||
|
&i.CompanySlug,
|
||||||
&i.TotalBets,
|
&i.TotalBets,
|
||||||
&i.TotalStake,
|
&i.TotalStake,
|
||||||
&i.DeductedStake,
|
&i.DeductedStake,
|
||||||
|
|
@ -87,7 +105,7 @@ func (q *Queries) GetCompanyStats(ctx context.Context, arg GetCompanyStatsParams
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetCompanyStatsByID = `-- name: GetCompanyStatsByID :many
|
const GetCompanyStatsByID = `-- name: GetCompanyStatsByID :many
|
||||||
SELECT company_id, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, updated_at
|
SELECT company_id, company_name, company_slug, interval_start, total_bets, total_stake, deducted_stake, total_cash_out, total_cash_backs, number_of_unsettled, total_unsettled_amount, total_admins, total_managers, total_cashiers, total_customers, total_approvers, total_branches, updated_at
|
||||||
FROM company_stats
|
FROM company_stats
|
||||||
WHERE company_id = $1
|
WHERE company_id = $1
|
||||||
ORDER BY interval_start DESC
|
ORDER BY interval_start DESC
|
||||||
|
|
@ -104,6 +122,8 @@ func (q *Queries) GetCompanyStatsByID(ctx context.Context, companyID int64) ([]C
|
||||||
var i CompanyStat
|
var i CompanyStat
|
||||||
if err := rows.Scan(
|
if err := rows.Scan(
|
||||||
&i.CompanyID,
|
&i.CompanyID,
|
||||||
|
&i.CompanyName,
|
||||||
|
&i.CompanySlug,
|
||||||
&i.IntervalStart,
|
&i.IntervalStart,
|
||||||
&i.TotalBets,
|
&i.TotalBets,
|
||||||
&i.TotalStake,
|
&i.TotalStake,
|
||||||
|
|
@ -202,6 +222,8 @@ branch_stats AS (
|
||||||
) -- Final combined aggregation
|
) -- Final combined aggregation
|
||||||
INSERT INTO company_stats (
|
INSERT INTO company_stats (
|
||||||
company_id,
|
company_id,
|
||||||
|
company_name,
|
||||||
|
company_slug,
|
||||||
interval_start,
|
interval_start,
|
||||||
total_bets,
|
total_bets,
|
||||||
total_stake,
|
total_stake,
|
||||||
|
|
@ -219,6 +241,8 @@ INSERT INTO company_stats (
|
||||||
updated_at
|
updated_at
|
||||||
)
|
)
|
||||||
SELECT c.id AS company_id,
|
SELECT c.id AS company_id,
|
||||||
|
c.name AS company_name,
|
||||||
|
c.slug AS company_slug,
|
||||||
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
COALESCE(b.total_bets, 0) AS total_bets,
|
COALESCE(b.total_bets, 0) AS total_bets,
|
||||||
COALESCE(b.total_stake, 0) AS total_stake,
|
COALESCE(b.total_stake, 0) AS total_stake,
|
||||||
|
|
@ -248,7 +272,7 @@ SET total_bets = EXCLUDED.total_bets,
|
||||||
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
total_unsettled_amount = EXCLUDED.total_unsettled_amount,
|
||||||
total_admins = EXCLUDED.total_admins,
|
total_admins = EXCLUDED.total_admins,
|
||||||
total_managers = EXCLUDED.total_managers,
|
total_managers = EXCLUDED.total_managers,
|
||||||
total_cashiers = EXCLUDED.total_cashiers,
|
SETtotal_cashiers = EXCLUDED.total_cashiers,
|
||||||
total_customers = EXCLUDED.total_customers,
|
total_customers = EXCLUDED.total_customers,
|
||||||
total_approvers = EXCLUDED.total_approvers,
|
total_approvers = EXCLUDED.total_approvers,
|
||||||
total_branches = EXCLUDED.total_branches,
|
total_branches = EXCLUDED.total_branches,
|
||||||
|
|
|
||||||
117
gen/db/models.go
117
gen/db/models.go
|
|
@ -103,22 +103,31 @@ type BranchCashier struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchDetail struct {
|
type BranchDetail struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Location string `json:"location"`
|
Location string `json:"location"`
|
||||||
ProfitPercent float32 `json:"profit_percent"`
|
ProfitPercent float32 `json:"profit_percent"`
|
||||||
IsActive bool `json:"is_active"`
|
IsActive bool `json:"is_active"`
|
||||||
WalletID int64 `json:"wallet_id"`
|
WalletID int64 `json:"wallet_id"`
|
||||||
BranchManagerID int64 `json:"branch_manager_id"`
|
BranchManagerID int64 `json:"branch_manager_id"`
|
||||||
CompanyID int64 `json:"company_id"`
|
CompanyID int64 `json:"company_id"`
|
||||||
IsSelfOwned bool `json:"is_self_owned"`
|
IsSelfOwned bool `json:"is_self_owned"`
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
ManagerName interface{} `json:"manager_name"`
|
ManagerName interface{} `json:"manager_name"`
|
||||||
ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"`
|
ManagerPhoneNumber pgtype.Text `json:"manager_phone_number"`
|
||||||
Balance pgtype.Int8 `json:"balance"`
|
Balance pgtype.Int8 `json:"balance"`
|
||||||
WalletIsActive pgtype.Bool `json:"wallet_is_active"`
|
WalletIsActive pgtype.Bool `json:"wallet_is_active"`
|
||||||
CompanyName string `json:"company_name"`
|
CompanyName string `json:"company_name"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
DeductedStake int64 `json:"deducted_stake"`
|
||||||
|
TotalCashOut int64 `json:"total_cash_out"`
|
||||||
|
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||||
|
TotalCashiers int64 `json:"total_cashiers"`
|
||||||
|
StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchLocation struct {
|
type BranchLocation struct {
|
||||||
|
|
@ -134,6 +143,24 @@ type BranchOperation struct {
|
||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchStat struct {
|
||||||
|
BranchID int64 `json:"branch_id"`
|
||||||
|
BranchName string `json:"branch_name"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
|
CompanySlug string `json:"company_slug"`
|
||||||
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
DeductedStake int64 `json:"deducted_stake"`
|
||||||
|
TotalCashOut int64 `json:"total_cash_out"`
|
||||||
|
TotalCashBacks int64 `json:"total_cash_backs"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||||
|
TotalCashiers int64 `json:"total_cashiers"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type CompaniesDetail struct {
|
type CompaniesDetail struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|
@ -222,6 +249,8 @@ type CompanySetting struct {
|
||||||
|
|
||||||
type CompanyStat struct {
|
type CompanyStat struct {
|
||||||
CompanyID int64 `json:"company_id"`
|
CompanyID int64 `json:"company_id"`
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
|
CompanySlug string `json:"company_slug"`
|
||||||
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
TotalBets int64 `json:"total_bets"`
|
TotalBets int64 `json:"total_bets"`
|
||||||
TotalStake int64 `json:"total_stake"`
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
|
@ -249,20 +278,29 @@ type CustomerWallet struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomerWalletDetail struct {
|
type CustomerWalletDetail struct {
|
||||||
ID int64 `json:"id"`
|
ID int64 `json:"id"`
|
||||||
CustomerID int64 `json:"customer_id"`
|
CustomerID int64 `json:"customer_id"`
|
||||||
RegularID int64 `json:"regular_id"`
|
RegularID int64 `json:"regular_id"`
|
||||||
RegularBalance int64 `json:"regular_balance"`
|
RegularBalance int64 `json:"regular_balance"`
|
||||||
StaticID int64 `json:"static_id"`
|
StaticID int64 `json:"static_id"`
|
||||||
StaticBalance int64 `json:"static_balance"`
|
StaticBalance int64 `json:"static_balance"`
|
||||||
RegularIsActive bool `json:"regular_is_active"`
|
RegularIsActive bool `json:"regular_is_active"`
|
||||||
StaticIsActive bool `json:"static_is_active"`
|
StaticIsActive bool `json:"static_is_active"`
|
||||||
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
|
RegularUpdatedAt pgtype.Timestamp `json:"regular_updated_at"`
|
||||||
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
|
StaticUpdatedAt pgtype.Timestamp `json:"static_updated_at"`
|
||||||
CreatedAt pgtype.Timestamp `json:"created_at"`
|
CreatedAt pgtype.Timestamp `json:"created_at"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
PhoneNumber pgtype.Text `json:"phone_number"`
|
PhoneNumber pgtype.Text `json:"phone_number"`
|
||||||
|
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||||
|
TotalTransactions int64 `json:"total_transactions"`
|
||||||
|
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||||
|
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||||
|
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||||
|
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||||
|
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||||
|
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||||
|
StatsUpdatedAt pgtype.Timestamp `json:"stats_updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type DirectDeposit struct {
|
type DirectDeposit struct {
|
||||||
|
|
@ -691,6 +729,7 @@ type ReportRequestDetail struct {
|
||||||
CompanySlug pgtype.Text `json:"company_slug"`
|
CompanySlug pgtype.Text `json:"company_slug"`
|
||||||
RequesterFirstName pgtype.Text `json:"requester_first_name"`
|
RequesterFirstName pgtype.Text `json:"requester_first_name"`
|
||||||
RequesterLastName pgtype.Text `json:"requester_last_name"`
|
RequesterLastName pgtype.Text `json:"requester_last_name"`
|
||||||
|
RequesterRole pgtype.Text `json:"requester_role"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReportedIssue struct {
|
type ReportedIssue struct {
|
||||||
|
|
@ -1038,6 +1077,24 @@ type Wallet struct {
|
||||||
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WalletStat struct {
|
||||||
|
WalletID int64 `json:"wallet_id"`
|
||||||
|
WalletUserID int64 `json:"wallet_user_id"`
|
||||||
|
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||||
|
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||||
|
WalletType string `json:"wallet_type"`
|
||||||
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
|
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||||
|
TotalTransactions int64 `json:"total_transactions"`
|
||||||
|
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||||
|
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||||
|
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||||
|
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||||
|
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||||
|
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
type WalletThresholdNotification struct {
|
type WalletThresholdNotification struct {
|
||||||
CompanyID int64 `json:"company_id"`
|
CompanyID int64 `json:"company_id"`
|
||||||
Threshold float64 `json:"threshold"`
|
Threshold float64 `json:"threshold"`
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ func (q *Queries) CreateReportRequest(ctx context.Context, arg CreateReportReque
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetAllReportRequests = `-- name: GetAllReportRequests :many
|
const GetAllReportRequests = `-- name: GetAllReportRequests :many
|
||||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||||
FROM report_request_detail
|
FROM report_request_detail
|
||||||
WHERE (
|
WHERE (
|
||||||
company_id = $1
|
company_id = $1
|
||||||
|
|
@ -115,6 +115,7 @@ func (q *Queries) GetAllReportRequests(ctx context.Context, arg GetAllReportRequ
|
||||||
&i.CompanySlug,
|
&i.CompanySlug,
|
||||||
&i.RequesterFirstName,
|
&i.RequesterFirstName,
|
||||||
&i.RequesterLastName,
|
&i.RequesterLastName,
|
||||||
|
&i.RequesterRole,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -127,7 +128,7 @@ func (q *Queries) GetAllReportRequests(ctx context.Context, arg GetAllReportRequ
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetReportRequestByID = `-- name: GetReportRequestByID :one
|
const GetReportRequestByID = `-- name: GetReportRequestByID :one
|
||||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||||
FROM report_request_detail
|
FROM report_request_detail
|
||||||
WHERE id = $1
|
WHERE id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -150,12 +151,13 @@ func (q *Queries) GetReportRequestByID(ctx context.Context, id int64) (ReportReq
|
||||||
&i.CompanySlug,
|
&i.CompanySlug,
|
||||||
&i.RequesterFirstName,
|
&i.RequesterFirstName,
|
||||||
&i.RequesterLastName,
|
&i.RequesterLastName,
|
||||||
|
&i.RequesterRole,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetReportRequestByRequestedByID = `-- name: GetReportRequestByRequestedByID :many
|
const GetReportRequestByRequestedByID = `-- name: GetReportRequestByRequestedByID :many
|
||||||
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name
|
SELECT id, company_id, requested_by, file_path, type, status, metadata, reject_reason, created_at, completed_at, company_name, company_slug, requester_first_name, requester_last_name, requester_role
|
||||||
FROM report_request_detail
|
FROM report_request_detail
|
||||||
WHERE requested_by = $1
|
WHERE requested_by = $1
|
||||||
AND (
|
AND (
|
||||||
|
|
@ -208,6 +210,7 @@ func (q *Queries) GetReportRequestByRequestedByID(ctx context.Context, arg GetRe
|
||||||
&i.CompanySlug,
|
&i.CompanySlug,
|
||||||
&i.RequesterFirstName,
|
&i.RequesterFirstName,
|
||||||
&i.RequesterLastName,
|
&i.RequesterLastName,
|
||||||
|
&i.RequesterRole,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func (q *Queries) GetAllBranchWallets(ctx context.Context) ([]GetAllBranchWallet
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetAllCustomerWallet = `-- name: GetAllCustomerWallet :many
|
const GetAllCustomerWallet = `-- name: GetAllCustomerWallet :many
|
||||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at
|
||||||
FROM customer_wallet_details
|
FROM customer_wallet_details
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -174,6 +174,15 @@ func (q *Queries) GetAllCustomerWallet(ctx context.Context) ([]CustomerWalletDet
|
||||||
&i.FirstName,
|
&i.FirstName,
|
||||||
&i.LastName,
|
&i.LastName,
|
||||||
&i.PhoneNumber,
|
&i.PhoneNumber,
|
||||||
|
&i.NumberOfTransactions,
|
||||||
|
&i.TotalTransactions,
|
||||||
|
&i.NumberOfDeposits,
|
||||||
|
&i.TotalDepositsAmount,
|
||||||
|
&i.NumberOfWithdraws,
|
||||||
|
&i.TotalWithdrawsAmount,
|
||||||
|
&i.NumberOfTransfers,
|
||||||
|
&i.TotalTransfersAmount,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -287,7 +296,7 @@ func (q *Queries) GetCompanyByWalletID(ctx context.Context, walletID int64) (Get
|
||||||
}
|
}
|
||||||
|
|
||||||
const GetCustomerWallet = `-- name: GetCustomerWallet :one
|
const GetCustomerWallet = `-- name: GetCustomerWallet :one
|
||||||
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number
|
SELECT id, customer_id, regular_id, regular_balance, static_id, static_balance, regular_is_active, static_is_active, regular_updated_at, static_updated_at, created_at, first_name, last_name, phone_number, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, stats_updated_at
|
||||||
FROM customer_wallet_details
|
FROM customer_wallet_details
|
||||||
WHERE customer_id = $1
|
WHERE customer_id = $1
|
||||||
`
|
`
|
||||||
|
|
@ -310,6 +319,15 @@ func (q *Queries) GetCustomerWallet(ctx context.Context, customerID int64) (Cust
|
||||||
&i.FirstName,
|
&i.FirstName,
|
||||||
&i.LastName,
|
&i.LastName,
|
||||||
&i.PhoneNumber,
|
&i.PhoneNumber,
|
||||||
|
&i.NumberOfTransactions,
|
||||||
|
&i.TotalTransactions,
|
||||||
|
&i.NumberOfDeposits,
|
||||||
|
&i.TotalDepositsAmount,
|
||||||
|
&i.NumberOfWithdraws,
|
||||||
|
&i.TotalWithdrawsAmount,
|
||||||
|
&i.NumberOfTransfers,
|
||||||
|
&i.TotalTransfersAmount,
|
||||||
|
&i.StatsUpdatedAt,
|
||||||
)
|
)
|
||||||
return i, err
|
return i, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
248
gen/db/wallet_stats.sql.go
Normal file
248
gen/db/wallet_stats.sql.go
Normal file
|
|
@ -0,0 +1,248 @@
|
||||||
|
// Code generated by sqlc. DO NOT EDIT.
|
||||||
|
// versions:
|
||||||
|
// sqlc v1.29.0
|
||||||
|
// source: wallet_stats.sql
|
||||||
|
|
||||||
|
package dbgen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
|
)
|
||||||
|
|
||||||
|
const GetWalletStats = `-- name: GetWalletStats :many
|
||||||
|
SELECT DATE_TRUNC($1, interval_start)::timestamp AS interval_start,
|
||||||
|
wallet_stats.wallet_id,
|
||||||
|
wallet_stats.wallet_user_id,
|
||||||
|
wallet_stats.wallet_user_first_name,
|
||||||
|
wallet_stats.wallet_user_last_name,
|
||||||
|
wallet_stats.wallet_type,
|
||||||
|
wallet_stats.number_of_transactions,
|
||||||
|
wallet_stats.total_transactions,
|
||||||
|
wallet_stats.number_of_deposits,
|
||||||
|
wallet_stats.total_deposits_amount,
|
||||||
|
wallet_stats.number_of_withdraws,
|
||||||
|
wallet_stats.total_withdraws_amount,
|
||||||
|
wallet_stats.number_of_transfers,
|
||||||
|
wallet_stats.total_transfers_amount,
|
||||||
|
wallet_stats.updated_at
|
||||||
|
FROM wallet_stats
|
||||||
|
WHERE (
|
||||||
|
wallet_stats.wallet_user_id = $2
|
||||||
|
OR $2 IS NULL
|
||||||
|
)
|
||||||
|
GROUP BY interval_start
|
||||||
|
ORDER BY interval_start DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
type GetWalletStatsParams struct {
|
||||||
|
Interval pgtype.Text `json:"interval"`
|
||||||
|
UserID pgtype.Int8 `json:"user_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GetWalletStatsRow struct {
|
||||||
|
IntervalStart pgtype.Timestamp `json:"interval_start"`
|
||||||
|
WalletID int64 `json:"wallet_id"`
|
||||||
|
WalletUserID int64 `json:"wallet_user_id"`
|
||||||
|
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||||
|
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||||
|
WalletType string `json:"wallet_type"`
|
||||||
|
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||||
|
TotalTransactions int64 `json:"total_transactions"`
|
||||||
|
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||||
|
TotalDepositsAmount int64 `json:"total_deposits_amount"`
|
||||||
|
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||||
|
TotalWithdrawsAmount int64 `json:"total_withdraws_amount"`
|
||||||
|
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||||
|
TotalTransfersAmount int64 `json:"total_transfers_amount"`
|
||||||
|
UpdatedAt pgtype.Timestamp `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetWalletStats(ctx context.Context, arg GetWalletStatsParams) ([]GetWalletStatsRow, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetWalletStats, arg.Interval, arg.UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []GetWalletStatsRow
|
||||||
|
for rows.Next() {
|
||||||
|
var i GetWalletStatsRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.IntervalStart,
|
||||||
|
&i.WalletID,
|
||||||
|
&i.WalletUserID,
|
||||||
|
&i.WalletUserFirstName,
|
||||||
|
&i.WalletUserLastName,
|
||||||
|
&i.WalletType,
|
||||||
|
&i.NumberOfTransactions,
|
||||||
|
&i.TotalTransactions,
|
||||||
|
&i.NumberOfDeposits,
|
||||||
|
&i.TotalDepositsAmount,
|
||||||
|
&i.NumberOfWithdraws,
|
||||||
|
&i.TotalWithdrawsAmount,
|
||||||
|
&i.NumberOfTransfers,
|
||||||
|
&i.TotalTransfersAmount,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetWalletStatsByID = `-- name: GetWalletStatsByID :many
|
||||||
|
SELECT wallet_id, wallet_user_id, wallet_user_first_name, wallet_user_last_name, wallet_type, interval_start, number_of_transactions, total_transactions, number_of_deposits, total_deposits_amount, number_of_withdraws, total_withdraws_amount, number_of_transfers, total_transfers_amount, updated_at
|
||||||
|
FROM wallet_stats
|
||||||
|
WHERE wallet_id = $1
|
||||||
|
ORDER BY interval_start DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) GetWalletStatsByID(ctx context.Context, walletID int64) ([]WalletStat, error) {
|
||||||
|
rows, err := q.db.Query(ctx, GetWalletStatsByID, walletID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
var items []WalletStat
|
||||||
|
for rows.Next() {
|
||||||
|
var i WalletStat
|
||||||
|
if err := rows.Scan(
|
||||||
|
&i.WalletID,
|
||||||
|
&i.WalletUserID,
|
||||||
|
&i.WalletUserFirstName,
|
||||||
|
&i.WalletUserLastName,
|
||||||
|
&i.WalletType,
|
||||||
|
&i.IntervalStart,
|
||||||
|
&i.NumberOfTransactions,
|
||||||
|
&i.TotalTransactions,
|
||||||
|
&i.NumberOfDeposits,
|
||||||
|
&i.TotalDepositsAmount,
|
||||||
|
&i.NumberOfWithdraws,
|
||||||
|
&i.TotalWithdrawsAmount,
|
||||||
|
&i.NumberOfTransfers,
|
||||||
|
&i.TotalTransfersAmount,
|
||||||
|
&i.UpdatedAt,
|
||||||
|
); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
items = append(items, i)
|
||||||
|
}
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return items, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateWalletStats = `-- name: UpdateWalletStats :exec
|
||||||
|
WITH all_transfers AS (
|
||||||
|
SELECT sender_wallet_id AS wallet_id,
|
||||||
|
amount,
|
||||||
|
type
|
||||||
|
FROM wallet_transfer
|
||||||
|
WHERE sender_wallet_id IS NOT NULL
|
||||||
|
UNION ALL
|
||||||
|
SELECT receiver_wallet_id AS wallet_id,
|
||||||
|
amount,
|
||||||
|
type
|
||||||
|
FROM wallet_transfer
|
||||||
|
WHERE receiver_wallet_id IS NOT NULL
|
||||||
|
),
|
||||||
|
transfer_stats AS (
|
||||||
|
SELECT wallet_id,
|
||||||
|
COUNT(*) AS number_of_transactions,
|
||||||
|
COALESCE(SUM(amount), 0) AS total_transactions,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'deposit'
|
||||||
|
) AS number_of_deposits,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'deposit' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_deposits_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'withdraw'
|
||||||
|
) AS number_of_withdraws,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'withdraw' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_withdraws_amount,
|
||||||
|
COUNT(*) FILTER (
|
||||||
|
WHERE type = 'wallet'
|
||||||
|
) AS number_of_transfers,
|
||||||
|
COALESCE(
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN type = 'wallet' THEN amount
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
),
|
||||||
|
0
|
||||||
|
) AS total_transfers_amount
|
||||||
|
FROM all_transfers
|
||||||
|
GROUP BY wallet_id
|
||||||
|
)
|
||||||
|
INSERT INTO wallet_stats(
|
||||||
|
wallet_id,
|
||||||
|
wallet_user_id,
|
||||||
|
wallet_user_first_name,
|
||||||
|
wallet_user_last_name,
|
||||||
|
wallet_type,
|
||||||
|
interval_start,
|
||||||
|
number_of_transactions,
|
||||||
|
total_transactions,
|
||||||
|
number_of_deposits,
|
||||||
|
total_deposits_amount,
|
||||||
|
number_of_withdraws,
|
||||||
|
total_withdraws_amount,
|
||||||
|
number_of_transfers,
|
||||||
|
total_transfers_amount,
|
||||||
|
updated_at
|
||||||
|
)
|
||||||
|
SELECT w.id AS wallet_id,
|
||||||
|
w.user_id AS wallet_user_id,
|
||||||
|
u.first_name AS wallet_user_first_name,
|
||||||
|
u.last_name AS wallet_user_last_name,
|
||||||
|
w.type AS wallet_type,
|
||||||
|
DATE_TRUNC('day', NOW() AT TIME ZONE 'UTC') AS interval_start,
|
||||||
|
COALESCE(ts.number_of_transactions, 0) AS number_of_transactions,
|
||||||
|
COALESCE(ts.total_transactions, 0) AS total_transactions,
|
||||||
|
COALESCE(ts.number_of_deposits, 0) AS number_of_deposits,
|
||||||
|
COALESCE(ts.total_deposits_amount, 0) AS total_deposits_amount,
|
||||||
|
COALESCE(ts.number_of_withdraws, 0) AS number_of_withdraws,
|
||||||
|
COALESCE(ts.total_withdraws_amount, 0) AS total_withdraws_amount,
|
||||||
|
COALESCE(ts.number_of_transfers, 0) AS number_of_transfers,
|
||||||
|
COALESCE(ts.total_transfers_amount, 0) AS total_transfers_amount,
|
||||||
|
NOW() AS updated_at
|
||||||
|
FROM wallets w
|
||||||
|
LEFT JOIN users u ON u.id = w.user_id
|
||||||
|
LEFT JOIN transfer_stats ts ON ts.wallet_id = w.id ON CONFLICT (wallet_id, interval_start) DO
|
||||||
|
UPDATE
|
||||||
|
SET number_of_transactions = EXCLUDED.number_of_transactions,
|
||||||
|
total_transactions = EXCLUDED.total_transactions,
|
||||||
|
number_of_deposits = EXCLUDED.number_of_deposits,
|
||||||
|
total_deposits_amount = EXCLUDED.total_deposits_amount,
|
||||||
|
number_of_withdraws = EXCLUDED.number_of_withdraws,
|
||||||
|
total_withdraws_amount = EXCLUDED.total_withdraws_amount,
|
||||||
|
number_of_transfers = EXCLUDED.number_of_transfers,
|
||||||
|
total_transfers_amount = EXCLUDED.total_transfers_amount,
|
||||||
|
updated_at = EXCLUDED.updated_at
|
||||||
|
`
|
||||||
|
|
||||||
|
func (q *Queries) UpdateWalletStats(ctx context.Context) error {
|
||||||
|
_, err := q.db.Exec(ctx, UpdateWalletStats)
|
||||||
|
return err
|
||||||
|
}
|
||||||
49
internal/domain/bet_stats.go
Normal file
49
internal/domain/bet_stats.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BetStatsByInterval struct {
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake int64 `json:"total_stake"`
|
||||||
|
ActiveBets int64 `json:"active_bets"`
|
||||||
|
TotalWins int64 `json:"total_wins"`
|
||||||
|
TotalLosses int64 `json:"total_losses"`
|
||||||
|
WinBalance int64 `json:"win_balance"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `json:"total_unsettled_amount"`
|
||||||
|
TotalShopBets int64 `json:"total_shop_bets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type BetStatsByIntervalFilter struct {
|
||||||
|
Interval ValidDateInterval
|
||||||
|
CompanyID ValidInt64
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBetStatsByInterval(stats dbgen.GetBetStatsByIntervalRow) BetStatsByInterval {
|
||||||
|
return BetStatsByInterval{
|
||||||
|
Date: stats.Date.Time,
|
||||||
|
TotalBets: stats.TotalBets,
|
||||||
|
TotalStake: stats.TotalStake,
|
||||||
|
ActiveBets: stats.ActiveBets,
|
||||||
|
TotalWins: stats.TotalWins,
|
||||||
|
TotalLosses: stats.TotalLosses,
|
||||||
|
WinBalance: stats.WinBalance,
|
||||||
|
NumberOfUnsettled: stats.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: stats.TotalUnsettledAmount,
|
||||||
|
TotalShopBets: stats.TotalShopBets,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBetStatsByIntervalList(stats []dbgen.GetBetStatsByIntervalRow) []BetStatsByInterval {
|
||||||
|
result := make([]BetStatsByInterval, len(stats))
|
||||||
|
for i, e := range stats {
|
||||||
|
result[i] = ConvertDBBetStatsByInterval(e)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
@ -32,19 +34,29 @@ type BranchFilter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchDetail struct {
|
type BranchDetail struct {
|
||||||
ID int64
|
ID int64
|
||||||
Name string
|
Name string
|
||||||
Location string
|
Location string
|
||||||
WalletID int64
|
WalletID int64
|
||||||
Balance Currency
|
Balance Currency
|
||||||
BranchManagerID int64
|
BranchManagerID int64
|
||||||
CompanyID int64
|
CompanyID int64
|
||||||
IsActive bool
|
IsActive bool
|
||||||
IsSelfOwned bool
|
IsSelfOwned bool
|
||||||
ManagerName string
|
ManagerName string
|
||||||
ManagerPhoneNumber string
|
ManagerPhoneNumber string
|
||||||
WalletIsActive bool
|
WalletIsActive bool
|
||||||
ProfitPercentage float32
|
ProfitPercentage float32
|
||||||
|
CompanyName string
|
||||||
|
TotalBets int64
|
||||||
|
TotalStake Currency
|
||||||
|
DeductedStake Currency
|
||||||
|
TotalCashOut Currency
|
||||||
|
TotalCashBacks Currency
|
||||||
|
NumberOfUnsettled int64
|
||||||
|
TotalUnsettledAmount Currency
|
||||||
|
TotalCashiers int64
|
||||||
|
StatsUpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type SupportedOperation struct {
|
type SupportedOperation struct {
|
||||||
|
|
@ -143,19 +155,29 @@ type BranchRes struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchDetailRes struct {
|
type BranchDetailRes struct {
|
||||||
ID int64 `json:"id" example:"1"`
|
ID int64 `json:"id" example:"1"`
|
||||||
Name string `json:"name" example:"4-kilo Branch"`
|
Name string `json:"name" example:"4-kilo Branch"`
|
||||||
Location string `json:"location" example:"Addis Ababa"`
|
Location string `json:"location" example:"Addis Ababa"`
|
||||||
WalletID int64 `json:"wallet_id" example:"1"`
|
WalletID int64 `json:"wallet_id" example:"1"`
|
||||||
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
|
BranchManagerID int64 `json:"branch_manager_id" example:"1"`
|
||||||
CompanyID int64 `json:"company_id" example:"1"`
|
CompanyID int64 `json:"company_id" example:"1"`
|
||||||
IsSelfOwned bool `json:"is_self_owned" example:"false"`
|
IsSelfOwned bool `json:"is_self_owned" example:"false"`
|
||||||
ManagerName string `json:"manager_name" example:"John Smith"`
|
ManagerName string `json:"manager_name" example:"John Smith"`
|
||||||
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
|
ManagerPhoneNumber string `json:"manager_phone_number" example:"0911111111"`
|
||||||
Balance float32 `json:"balance" example:"100.5"`
|
Balance float32 `json:"balance" example:"100.5"`
|
||||||
IsActive bool `json:"is_active" example:"false"`
|
IsActive bool `json:"is_active" example:"false"`
|
||||||
WalletIsActive bool `json:"is_wallet_active" example:"false"`
|
WalletIsActive bool `json:"is_wallet_active" example:"false"`
|
||||||
ProfitPercentage float32 `json:"profit_percentage" example:"0.1"`
|
ProfitPercentage float32 `json:"profit_percentage" example:"0.1"`
|
||||||
|
CompanyName string `json:"company_name" example:"fortune"`
|
||||||
|
TotalBets int64 `json:"total_bets"`
|
||||||
|
TotalStake float32 `json:"total_stake"`
|
||||||
|
DeductedStake float32 `json:"deducted_stake"`
|
||||||
|
TotalCashOut float32 `json:"total_cash_out"`
|
||||||
|
TotalCashBacks float32 `json:"total_cash_backs"`
|
||||||
|
NumberOfUnsettled int64 `json:"number_of_unsettled"`
|
||||||
|
TotalUnsettledAmount float32 `json:"total_unsettled_amount"`
|
||||||
|
TotalCashiers int64 `json:"total_cashiers"`
|
||||||
|
StatsUpdatedAt time.Time `json:"stats_updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertBranch(branch Branch) BranchRes {
|
func ConvertBranch(branch Branch) BranchRes {
|
||||||
|
|
@ -174,19 +196,29 @@ func ConvertBranch(branch Branch) BranchRes {
|
||||||
|
|
||||||
func ConvertBranchDetail(branch BranchDetail) BranchDetailRes {
|
func ConvertBranchDetail(branch BranchDetail) BranchDetailRes {
|
||||||
return BranchDetailRes{
|
return BranchDetailRes{
|
||||||
ID: branch.ID,
|
ID: branch.ID,
|
||||||
Name: branch.Name,
|
Name: branch.Name,
|
||||||
Location: branch.Location,
|
Location: branch.Location,
|
||||||
WalletID: branch.WalletID,
|
WalletID: branch.WalletID,
|
||||||
BranchManagerID: branch.BranchManagerID,
|
BranchManagerID: branch.BranchManagerID,
|
||||||
CompanyID: branch.CompanyID,
|
CompanyID: branch.CompanyID,
|
||||||
IsSelfOwned: branch.IsSelfOwned,
|
IsSelfOwned: branch.IsSelfOwned,
|
||||||
ManagerName: branch.ManagerName,
|
ManagerName: branch.ManagerName,
|
||||||
ManagerPhoneNumber: branch.ManagerPhoneNumber,
|
ManagerPhoneNumber: branch.ManagerPhoneNumber,
|
||||||
Balance: branch.Balance.Float32(),
|
Balance: branch.Balance.Float32(),
|
||||||
IsActive: branch.IsActive,
|
IsActive: branch.IsActive,
|
||||||
WalletIsActive: branch.WalletIsActive,
|
WalletIsActive: branch.WalletIsActive,
|
||||||
ProfitPercentage: branch.ProfitPercentage,
|
ProfitPercentage: branch.ProfitPercentage,
|
||||||
|
CompanyName: branch.CompanyName,
|
||||||
|
TotalBets: branch.TotalBets,
|
||||||
|
TotalStake: branch.TotalStake.Float32(),
|
||||||
|
DeductedStake: branch.DeductedStake.Float32(),
|
||||||
|
TotalCashOut: branch.TotalCashOut.Float32(),
|
||||||
|
TotalCashBacks: branch.TotalCashBacks.Float32(),
|
||||||
|
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: branch.TotalUnsettledAmount.Float32(),
|
||||||
|
TotalCashiers: branch.TotalCashiers,
|
||||||
|
StatsUpdatedAt: branch.StatsUpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -204,19 +236,29 @@ func ConvertCreateBranch(branch CreateBranch) dbgen.CreateBranchParams {
|
||||||
|
|
||||||
func ConvertDBBranchDetail(dbBranch dbgen.BranchDetail) BranchDetail {
|
func ConvertDBBranchDetail(dbBranch dbgen.BranchDetail) BranchDetail {
|
||||||
return BranchDetail{
|
return BranchDetail{
|
||||||
ID: dbBranch.ID,
|
ID: dbBranch.ID,
|
||||||
Name: dbBranch.Name,
|
Name: dbBranch.Name,
|
||||||
Location: dbBranch.Location,
|
Location: dbBranch.Location,
|
||||||
WalletID: dbBranch.WalletID,
|
WalletID: dbBranch.WalletID,
|
||||||
BranchManagerID: dbBranch.BranchManagerID,
|
BranchManagerID: dbBranch.BranchManagerID,
|
||||||
CompanyID: dbBranch.CompanyID,
|
CompanyID: dbBranch.CompanyID,
|
||||||
IsSelfOwned: dbBranch.IsSelfOwned,
|
IsSelfOwned: dbBranch.IsSelfOwned,
|
||||||
ManagerName: dbBranch.ManagerName.(string),
|
ManagerName: dbBranch.ManagerName.(string),
|
||||||
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
|
ManagerPhoneNumber: dbBranch.ManagerPhoneNumber.String,
|
||||||
Balance: Currency(dbBranch.Balance.Int64),
|
Balance: Currency(dbBranch.Balance.Int64),
|
||||||
IsActive: dbBranch.IsActive,
|
IsActive: dbBranch.IsActive,
|
||||||
WalletIsActive: dbBranch.WalletIsActive.Bool,
|
WalletIsActive: dbBranch.WalletIsActive.Bool,
|
||||||
ProfitPercentage: dbBranch.ProfitPercent,
|
ProfitPercentage: dbBranch.ProfitPercent,
|
||||||
|
CompanyName: dbBranch.CompanyName,
|
||||||
|
TotalBets: dbBranch.TotalBets,
|
||||||
|
TotalStake: Currency(dbBranch.TotalStake),
|
||||||
|
DeductedStake: Currency(dbBranch.DeductedStake),
|
||||||
|
TotalCashOut: Currency(dbBranch.TotalCashOut),
|
||||||
|
TotalCashBacks: Currency(dbBranch.TotalCashBacks),
|
||||||
|
NumberOfUnsettled: dbBranch.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: Currency(dbBranch.TotalUnsettledAmount),
|
||||||
|
TotalCashiers: dbBranch.TotalCashiers,
|
||||||
|
StatsUpdatedAt: dbBranch.StatsUpdatedAt.Time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,88 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
)
|
||||||
|
|
||||||
// Branch-level aggregated report
|
// Branch-level aggregated report
|
||||||
type BranchStats struct {
|
type BranchStat struct {
|
||||||
BranchID int64
|
IntervalStart time.Time
|
||||||
CompanyID int64
|
BranchID int64
|
||||||
TotalBets int64
|
BranchName string
|
||||||
TotalCashIn Currency
|
CompanyID int64
|
||||||
TotalCashOut Currency
|
CompanyName string
|
||||||
TotalCashBacks Currency
|
CompanySlug string
|
||||||
|
TotalBets int64
|
||||||
|
TotalStake Currency
|
||||||
|
DeductedStake Currency
|
||||||
|
TotalCashOut Currency
|
||||||
|
TotalCashBacks Currency
|
||||||
|
NumberOfUnsettled int64
|
||||||
|
TotalUnsettledAmount Currency
|
||||||
|
TotalCashiers int64
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type BranchStatFilter struct {
|
||||||
|
Interval ValidDateInterval
|
||||||
|
BranchID ValidInt64
|
||||||
|
CompanyID ValidInt64
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBranchStats(branch dbgen.BranchStat) BranchStat {
|
||||||
|
return BranchStat{
|
||||||
|
IntervalStart: branch.IntervalStart.Time,
|
||||||
|
BranchID: branch.BranchID,
|
||||||
|
BranchName: branch.BranchName,
|
||||||
|
CompanyID: branch.CompanyID,
|
||||||
|
CompanyName: branch.CompanyName,
|
||||||
|
CompanySlug: branch.CompanySlug,
|
||||||
|
TotalBets: branch.TotalBets,
|
||||||
|
TotalStake: Currency(branch.TotalStake),
|
||||||
|
DeductedStake: Currency(branch.DeductedStake),
|
||||||
|
TotalCashOut: Currency(branch.TotalCashOut),
|
||||||
|
TotalCashBacks: Currency(branch.TotalCashBacks),
|
||||||
|
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount),
|
||||||
|
TotalCashiers: branch.TotalCashiers,
|
||||||
|
UpdatedAt: branch.UpdatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBranchStatsList(stats []dbgen.BranchStat) []BranchStat {
|
||||||
|
result := make([]BranchStat, len(stats))
|
||||||
|
for i, stat := range stats {
|
||||||
|
result[i] = ConvertDBBranchStats(stat)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBranchStatsByInterval(branch dbgen.GetBranchStatsRow) BranchStat {
|
||||||
|
return BranchStat{
|
||||||
|
IntervalStart: branch.IntervalStart.Time,
|
||||||
|
BranchID: branch.BranchID,
|
||||||
|
BranchName: branch.BranchName,
|
||||||
|
CompanyID: branch.CompanyID,
|
||||||
|
CompanyName: branch.CompanyName,
|
||||||
|
CompanySlug: branch.CompanySlug,
|
||||||
|
TotalBets: branch.TotalBets,
|
||||||
|
TotalStake: Currency(branch.TotalStake),
|
||||||
|
DeductedStake: Currency(branch.DeductedStake),
|
||||||
|
TotalCashOut: Currency(branch.TotalCashOut),
|
||||||
|
TotalCashBacks: Currency(branch.TotalCashBacks),
|
||||||
|
NumberOfUnsettled: branch.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: Currency(branch.TotalUnsettledAmount),
|
||||||
|
TotalCashiers: branch.TotalCashiers,
|
||||||
|
UpdatedAt: branch.UpdatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBBranchStatsByIntervalList(stats []dbgen.GetBranchStatsRow) []BranchStat {
|
||||||
|
result := make([]BranchStat, len(stats))
|
||||||
|
for i, stat := range stats {
|
||||||
|
result[i] = ConvertDBBranchStatsByInterval(stat)
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -148,7 +148,7 @@ func ConvertGetCompany(company GetCompany) GetCompanyRes {
|
||||||
AdminPhoneNumber: company.AdminPhoneNumber,
|
AdminPhoneNumber: company.AdminPhoneNumber,
|
||||||
TotalBets: company.TotalBets,
|
TotalBets: company.TotalBets,
|
||||||
TotalStake: company.TotalStake.Float32(),
|
TotalStake: company.TotalStake.Float32(),
|
||||||
DeductedStake: company.DeductedPercentage,
|
DeductedStake: company.DeductedStake.Float32(),
|
||||||
TotalCashOut: company.TotalCashOut.Float32(),
|
TotalCashOut: company.TotalCashOut.Float32(),
|
||||||
TotalCashBacks: company.TotalCashBacks.Float32(),
|
TotalCashBacks: company.TotalCashBacks.Float32(),
|
||||||
NumberOfUnsettled: company.NumberOfUnsettled,
|
NumberOfUnsettled: company.NumberOfUnsettled,
|
||||||
|
|
@ -202,7 +202,7 @@ func ConvertDBCompanyDetails(dbCompany dbgen.CompaniesDetail) GetCompany {
|
||||||
IsActive: dbCompany.IsActive,
|
IsActive: dbCompany.IsActive,
|
||||||
TotalBets: dbCompany.TotalBets,
|
TotalBets: dbCompany.TotalBets,
|
||||||
TotalStake: Currency(dbCompany.TotalStake),
|
TotalStake: Currency(dbCompany.TotalStake),
|
||||||
DeductedStake: Currency(dbCompany.DeductedPercentage),
|
DeductedStake: Currency(dbCompany.DeductedStake),
|
||||||
TotalCashOut: Currency(dbCompany.TotalCashOut),
|
TotalCashOut: Currency(dbCompany.TotalCashOut),
|
||||||
TotalCashBacks: Currency(dbCompany.TotalCashBacks),
|
TotalCashBacks: Currency(dbCompany.TotalCashBacks),
|
||||||
NumberOfUnsettled: dbCompany.NumberOfUnsettled,
|
NumberOfUnsettled: dbCompany.NumberOfUnsettled,
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,10 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type CompanyStat struct {
|
type CompanyStat struct {
|
||||||
CompanyID int64
|
|
||||||
IntervalStart time.Time
|
IntervalStart time.Time
|
||||||
|
CompanyID int64
|
||||||
|
CompanyName string
|
||||||
|
CompanySlug string
|
||||||
TotalBets int64
|
TotalBets int64
|
||||||
TotalStake Currency
|
TotalStake Currency
|
||||||
DeductedStake Currency
|
DeductedStake Currency
|
||||||
|
|
@ -24,8 +26,10 @@ type CompanyStat struct {
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
type CompanyStatRes struct {
|
type CompanyStatRes struct {
|
||||||
CompanyID int64 `json:"company_id"`
|
|
||||||
IntervalStart time.Time `json:"interval_start"`
|
IntervalStart time.Time `json:"interval_start"`
|
||||||
|
CompanyID int64 `json:"company_id"`
|
||||||
|
CompanyName string `json:"company_name"`
|
||||||
|
CompanySlug string `json:"company_slug"`
|
||||||
TotalBets int64 `json:"total_bets"`
|
TotalBets int64 `json:"total_bets"`
|
||||||
TotalStake float32 `json:"total_stake"`
|
TotalStake float32 `json:"total_stake"`
|
||||||
DeductedStake float32 `json:"deducted_stake"`
|
DeductedStake float32 `json:"deducted_stake"`
|
||||||
|
|
@ -49,7 +53,10 @@ type CompanyStatFilter struct {
|
||||||
|
|
||||||
func ConvertDBCompanyStats(company dbgen.CompanyStat) CompanyStat {
|
func ConvertDBCompanyStats(company dbgen.CompanyStat) CompanyStat {
|
||||||
return CompanyStat{
|
return CompanyStat{
|
||||||
|
IntervalStart: company.IntervalStart.Time,
|
||||||
CompanyID: company.CompanyID,
|
CompanyID: company.CompanyID,
|
||||||
|
CompanyName: company.CompanyName,
|
||||||
|
CompanySlug: company.CompanySlug,
|
||||||
TotalBets: company.TotalBets,
|
TotalBets: company.TotalBets,
|
||||||
TotalStake: Currency(company.TotalStake),
|
TotalStake: Currency(company.TotalStake),
|
||||||
DeductedStake: Currency(company.DeductedStake),
|
DeductedStake: Currency(company.DeductedStake),
|
||||||
|
|
@ -77,8 +84,10 @@ func ConvertDBCompanyStatsList(stats []dbgen.CompanyStat) []CompanyStat {
|
||||||
|
|
||||||
func ConvertDBCompanyStatsByInterval(company dbgen.GetCompanyStatsRow) CompanyStat {
|
func ConvertDBCompanyStatsByInterval(company dbgen.GetCompanyStatsRow) CompanyStat {
|
||||||
return CompanyStat{
|
return CompanyStat{
|
||||||
CompanyID: company.CompanyID,
|
|
||||||
IntervalStart: company.IntervalStart.Time,
|
IntervalStart: company.IntervalStart.Time,
|
||||||
|
CompanyID: company.CompanyID,
|
||||||
|
CompanyName: company.CompanyName,
|
||||||
|
CompanySlug: company.CompanySlug,
|
||||||
TotalBets: company.TotalBets,
|
TotalBets: company.TotalBets,
|
||||||
TotalStake: Currency(company.TotalStake),
|
TotalStake: Currency(company.TotalStake),
|
||||||
DeductedStake: Currency(company.DeductedStake),
|
DeductedStake: Currency(company.DeductedStake),
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,18 @@
|
||||||
package domain
|
package domain
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5/pgtype"
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidInterval = errors.New("invalid interval provided")
|
||||||
|
)
|
||||||
|
|
||||||
type DateInterval string
|
type DateInterval string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -30,18 +30,18 @@ type PaginatedFileResponse struct {
|
||||||
// EndDate time.Time
|
// EndDate time.Time
|
||||||
// }
|
// }
|
||||||
|
|
||||||
type ReportData struct {
|
// type ReportData struct {
|
||||||
TotalBets int64
|
// TotalBets int64
|
||||||
TotalCashIn float64
|
// TotalCashIn float64
|
||||||
TotalCashOut float64
|
// TotalCashOut float64
|
||||||
CashBacks float64
|
// CashBacks float64
|
||||||
Withdrawals float64
|
// Withdrawals float64
|
||||||
Deposits float64
|
// Deposits float64
|
||||||
TotalTickets int64
|
// TotalTickets int64
|
||||||
VirtualGameStats []VirtualGameStat
|
// VirtualGameStats []VirtualGameStat
|
||||||
CompanyReports []CompanyStat
|
// CompanyReports []CompanyStat
|
||||||
BranchReports []BranchStats
|
// BranchReports []BranchStats
|
||||||
}
|
// }
|
||||||
|
|
||||||
type VirtualGameStat struct {
|
type VirtualGameStat struct {
|
||||||
GameName string
|
GameName string
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ type ReportRequestDetail struct {
|
||||||
RequestedBy ValidInt64
|
RequestedBy ValidInt64
|
||||||
RequesterFirstName ValidString
|
RequesterFirstName ValidString
|
||||||
RequesterLastName ValidString
|
RequesterLastName ValidString
|
||||||
|
RequesterRole ValidRole
|
||||||
FilePath ValidString
|
FilePath ValidString
|
||||||
Type ReportRequestType
|
Type ReportRequestType
|
||||||
Status ReportRequestStatus
|
Status ReportRequestStatus
|
||||||
|
|
@ -69,6 +70,7 @@ type ReportRequestDetailRes struct {
|
||||||
RequestedBy *int64 `json:"requested_by,omitempty"`
|
RequestedBy *int64 `json:"requested_by,omitempty"`
|
||||||
RequesterFirstName *string `json:"requester_first_name,omitempty"`
|
RequesterFirstName *string `json:"requester_first_name,omitempty"`
|
||||||
RequesterLastName *string `json:"requester_last_name,omitempty"`
|
RequesterLastName *string `json:"requester_last_name,omitempty"`
|
||||||
|
RequesterRole *string `json:"requester_role,omitempty"`
|
||||||
FilePath *string `json:"file_path,omitempty"`
|
FilePath *string `json:"file_path,omitempty"`
|
||||||
Type ReportRequestType `json:"type"`
|
Type ReportRequestType `json:"type"`
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
|
|
@ -173,6 +175,10 @@ func ConvertDBReportRequestDetail(report dbgen.ReportRequestDetail) (ReportReque
|
||||||
Value: report.RequesterLastName.String,
|
Value: report.RequesterLastName.String,
|
||||||
Valid: report.RequesterLastName.Valid,
|
Valid: report.RequesterLastName.Valid,
|
||||||
},
|
},
|
||||||
|
RequesterRole: ValidRole{
|
||||||
|
Value: Role(report.RequesterRole.String),
|
||||||
|
Valid: report.RequesterRole.Valid,
|
||||||
|
},
|
||||||
FilePath: ValidString{
|
FilePath: ValidString{
|
||||||
Value: report.FilePath.String,
|
Value: report.FilePath.String,
|
||||||
Valid: report.FilePath.Valid,
|
Valid: report.FilePath.Valid,
|
||||||
|
|
@ -257,6 +263,9 @@ func ConvertReportRequestDetail(request ReportRequestDetail) ReportRequestDetail
|
||||||
if request.RequesterLastName.Valid {
|
if request.RequesterLastName.Valid {
|
||||||
res.RequesterLastName = &request.RequesterLastName.Value
|
res.RequesterLastName = &request.RequesterLastName.Value
|
||||||
}
|
}
|
||||||
|
if request.RequesterRole.Valid {
|
||||||
|
res.RequesterRole = (*string)(&request.RequesterRole.Value)
|
||||||
|
}
|
||||||
if request.CompanyID.Valid {
|
if request.CompanyID.Valid {
|
||||||
res.CompanyID = &request.CompanyID.Value
|
res.CompanyID = &request.CompanyID.Value
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,13 +10,16 @@ type ReportRequestType string
|
||||||
|
|
||||||
var (
|
var (
|
||||||
EventIntervalReportRequest ReportRequestType = "event_interval"
|
EventIntervalReportRequest ReportRequestType = "event_interval"
|
||||||
EventBetReportRequest ReportRequestType = "event_bet"
|
// EventBetReportRequest ReportRequestType = "event_bet"
|
||||||
CompanyReportRequest ReportRequestType = "company"
|
CompanyIntervalReportRequest ReportRequestType = "company_interval"
|
||||||
|
BranchIntervalReportRequest ReportRequestType = "branch_interval"
|
||||||
|
BetIntervalReportRequest ReportRequestType = "bet_interval"
|
||||||
|
WalletIntervalReportRequest ReportRequestType = "wallet_interval"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (r ReportRequestType) IsValid() bool {
|
func (r ReportRequestType) IsValid() bool {
|
||||||
switch r {
|
switch r {
|
||||||
case EventIntervalReportRequest, CompanyReportRequest:
|
case EventIntervalReportRequest, CompanyIntervalReportRequest:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -39,20 +39,29 @@ type CustomerWallet struct {
|
||||||
CustomerID int64
|
CustomerID int64
|
||||||
}
|
}
|
||||||
type GetCustomerWallet struct {
|
type GetCustomerWallet struct {
|
||||||
ID int64
|
ID int64
|
||||||
RegularID int64
|
RegularID int64
|
||||||
RegularBalance Currency
|
RegularBalance Currency
|
||||||
StaticID int64
|
StaticID int64
|
||||||
StaticBalance Currency
|
StaticBalance Currency
|
||||||
CustomerID int64
|
CustomerID int64
|
||||||
RegularIsActive bool
|
RegularIsActive bool
|
||||||
StaticIsActive bool
|
StaticIsActive bool
|
||||||
RegularUpdatedAt time.Time
|
RegularUpdatedAt time.Time
|
||||||
StaticUpdatedAt time.Time
|
StaticUpdatedAt time.Time
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
FirstName string
|
FirstName string
|
||||||
LastName string
|
LastName string
|
||||||
PhoneNumber string
|
PhoneNumber string
|
||||||
|
NumberOfTransactions int64
|
||||||
|
TotalTransactions Currency
|
||||||
|
NumberOfDeposits int64
|
||||||
|
TotalDepositsAmount Currency
|
||||||
|
NumberOfWithdraws int64
|
||||||
|
TotalWithdrawsAmount Currency
|
||||||
|
NumberOfTransfers int64
|
||||||
|
TotalTransfersAmount Currency
|
||||||
|
UpdatedAt time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
type BranchWallet struct {
|
type BranchWallet struct {
|
||||||
|
|
@ -134,21 +143,28 @@ func ConvertCreateCustomerWallet(customerWallet CreateCustomerWallet) dbgen.Crea
|
||||||
|
|
||||||
func ConvertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) GetCustomerWallet {
|
func ConvertDBGetCustomerWallet(customerWallet dbgen.CustomerWalletDetail) GetCustomerWallet {
|
||||||
return GetCustomerWallet{
|
return GetCustomerWallet{
|
||||||
ID: customerWallet.ID,
|
ID: customerWallet.ID,
|
||||||
RegularID: customerWallet.RegularID,
|
RegularID: customerWallet.RegularID,
|
||||||
RegularBalance: Currency(customerWallet.RegularBalance),
|
RegularBalance: Currency(customerWallet.RegularBalance),
|
||||||
StaticID: customerWallet.StaticID,
|
StaticID: customerWallet.StaticID,
|
||||||
StaticBalance: Currency(customerWallet.StaticBalance),
|
StaticBalance: Currency(customerWallet.StaticBalance),
|
||||||
CustomerID: customerWallet.CustomerID,
|
CustomerID: customerWallet.CustomerID,
|
||||||
RegularIsActive: customerWallet.RegularIsActive,
|
RegularIsActive: customerWallet.RegularIsActive,
|
||||||
StaticIsActive: customerWallet.StaticIsActive,
|
StaticIsActive: customerWallet.StaticIsActive,
|
||||||
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
|
RegularUpdatedAt: customerWallet.RegularUpdatedAt.Time,
|
||||||
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
|
StaticUpdatedAt: customerWallet.StaticUpdatedAt.Time,
|
||||||
CreatedAt: customerWallet.CreatedAt.Time,
|
CreatedAt: customerWallet.CreatedAt.Time,
|
||||||
FirstName: customerWallet.FirstName,
|
FirstName: customerWallet.FirstName,
|
||||||
LastName: customerWallet.LastName,
|
LastName: customerWallet.LastName,
|
||||||
PhoneNumber: customerWallet.PhoneNumber.String,
|
PhoneNumber: customerWallet.PhoneNumber.String,
|
||||||
|
NumberOfTransactions: customerWallet.NumberOfTransactions,
|
||||||
|
TotalTransactions: Currency(customerWallet.TotalTransactions),
|
||||||
|
NumberOfDeposits: customerWallet.NumberOfDeposits,
|
||||||
|
TotalDepositsAmount: Currency(customerWallet.TotalDepositsAmount),
|
||||||
|
NumberOfWithdraws: customerWallet.NumberOfWithdraws,
|
||||||
|
TotalWithdrawsAmount: Currency(customerWallet.TotalWithdrawsAmount),
|
||||||
|
NumberOfTransfers: customerWallet.NumberOfTransfers,
|
||||||
|
TotalTransfersAmount: Currency(customerWallet.TotalTransfersAmount),
|
||||||
|
UpdatedAt: customerWallet.StatsUpdatedAt.Time,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
108
internal/domain/wallet_stats.go
Normal file
108
internal/domain/wallet_stats.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package domain
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WalletStat struct {
|
||||||
|
IntervalStart time.Time
|
||||||
|
WalletID int64
|
||||||
|
WalletUserID int64
|
||||||
|
WalletUserFirstName string
|
||||||
|
WalletUserLastName string
|
||||||
|
WalletType string
|
||||||
|
NumberOfTransactions int64
|
||||||
|
TotalTransactions Currency
|
||||||
|
NumberOfDeposits int64
|
||||||
|
TotalDepositsAmount Currency
|
||||||
|
NumberOfWithdraws int64
|
||||||
|
TotalWithdrawsAmount Currency
|
||||||
|
NumberOfTransfers int64
|
||||||
|
TotalTransfersAmount Currency
|
||||||
|
UpdatedAt time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalletStatRes struct {
|
||||||
|
IntervalStart time.Time `json:"interval_start"`
|
||||||
|
WalletID int64 `json:"wallet_id"`
|
||||||
|
WalletUserID int64 `json:"wallet_user_id"`
|
||||||
|
WalletUserFirstName string `json:"wallet_user_first_name"`
|
||||||
|
WalletUserLastName string `json:"wallet_user_last_name"`
|
||||||
|
WalletType string `json:"wallet_type"`
|
||||||
|
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||||
|
TotalTransactions float32 `json:"total_transactions"`
|
||||||
|
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||||
|
TotalDepositsAmount float32 `json:"total_deposits_amount"`
|
||||||
|
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||||
|
TotalWithdrawsAmount float32 `json:"total_withdraws_amount"`
|
||||||
|
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||||
|
TotalTransfersAmount float32 `json:"total_transfers_amount"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type WalletStatFilter struct {
|
||||||
|
Interval ValidDateInterval
|
||||||
|
UserID ValidInt64
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBWalletStats(stats dbgen.WalletStat) WalletStat {
|
||||||
|
return WalletStat{
|
||||||
|
IntervalStart: stats.IntervalStart.Time,
|
||||||
|
WalletID: stats.WalletID,
|
||||||
|
WalletUserID: stats.WalletUserID,
|
||||||
|
WalletUserFirstName: stats.WalletUserFirstName,
|
||||||
|
WalletUserLastName: stats.WalletUserLastName,
|
||||||
|
WalletType: stats.WalletType,
|
||||||
|
NumberOfTransactions: stats.NumberOfTransactions,
|
||||||
|
TotalTransactions: Currency(stats.TotalTransactions),
|
||||||
|
NumberOfDeposits: stats.NumberOfDeposits,
|
||||||
|
TotalDepositsAmount: Currency(stats.TotalDepositsAmount),
|
||||||
|
NumberOfWithdraws: stats.NumberOfWithdraws,
|
||||||
|
TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount),
|
||||||
|
NumberOfTransfers: stats.NumberOfTransfers,
|
||||||
|
TotalTransfersAmount: Currency(stats.TotalTransfersAmount),
|
||||||
|
UpdatedAt: stats.UpdatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ConvertDBWalletStatsList(stats []dbgen.WalletStat) []WalletStat {
|
||||||
|
result := make([]WalletStat, len(stats))
|
||||||
|
for i, stat := range stats {
|
||||||
|
result[i] = ConvertDBWalletStats(stat)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertDBWalletStatsByInterval(stats dbgen.GetWalletStatsRow) WalletStat {
|
||||||
|
return WalletStat{
|
||||||
|
IntervalStart: stats.IntervalStart.Time,
|
||||||
|
WalletID: stats.WalletID,
|
||||||
|
WalletUserID: stats.WalletUserID,
|
||||||
|
WalletUserFirstName: stats.WalletUserFirstName,
|
||||||
|
WalletUserLastName: stats.WalletUserLastName,
|
||||||
|
WalletType: stats.WalletType,
|
||||||
|
NumberOfTransactions: stats.NumberOfTransactions,
|
||||||
|
TotalTransactions: Currency(stats.TotalTransactions),
|
||||||
|
NumberOfDeposits: stats.NumberOfDeposits,
|
||||||
|
TotalDepositsAmount: Currency(stats.TotalDepositsAmount),
|
||||||
|
NumberOfWithdraws: stats.NumberOfWithdraws,
|
||||||
|
TotalWithdrawsAmount: Currency(stats.TotalWithdrawsAmount),
|
||||||
|
NumberOfTransfers: stats.NumberOfTransfers,
|
||||||
|
TotalTransfersAmount: Currency(stats.TotalTransfersAmount),
|
||||||
|
UpdatedAt: stats.UpdatedAt.Time,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
func ConvertDBWalletStatsByIntervalList(stats []dbgen.GetWalletStatsRow) []WalletStat {
|
||||||
|
result := make([]WalletStat, len(stats))
|
||||||
|
for i, stat := range stats {
|
||||||
|
result[i] = ConvertDBWalletStatsByInterval(stat)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
package ports
|
package ports
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"time"
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BetStore interface {
|
type BetStore interface {
|
||||||
|
|
@ -50,4 +51,6 @@ type BetStore interface {
|
||||||
UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error
|
UpdateBetWithCashback(ctx context.Context, betID int64, cashbackStatus bool) error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BetStatStore interface {
|
||||||
|
GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,3 +32,9 @@ type BranchStore interface {
|
||||||
|
|
||||||
GetAllBranchLocations(ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error)
|
GetAllBranchLocations(ctx context.Context, query domain.ValidString) ([]domain.BranchLocation, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type BranchStatStore interface {
|
||||||
|
UpdateBranchStats(ctx context.Context) error
|
||||||
|
GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error)
|
||||||
|
GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,9 @@ type DirectDepositStore interface {
|
||||||
GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error)
|
GetDirectDepositsByStatus(ctx context.Context, status domain.DirectDepositStatus) ([]domain.DirectDeposit, error)
|
||||||
GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error)
|
GetCustomerDirectDeposits(ctx context.Context, customerID int64) ([]domain.DirectDeposit, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type WalletStatStore interface {
|
||||||
|
UpdateWalletStats(ctx context.Context) error
|
||||||
|
GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error)
|
||||||
|
GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error)
|
||||||
|
}
|
||||||
|
|
|
||||||
23
internal/repository/bet_stats.go
Normal file
23
internal/repository/bet_stats.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewBetStatStore(s *Store) ports.BetStatStore { return s }
|
||||||
|
|
||||||
|
func (s *Store) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) {
|
||||||
|
stats, err := s.queries.GetBetStatsByInterval(ctx, dbgen.GetBetStatsByIntervalParams{
|
||||||
|
Interval: filter.Interval.ToPG(),
|
||||||
|
CompanyID: filter.CompanyID.ToPG(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.ConvertDBBetStatsByIntervalList(stats), nil
|
||||||
|
}
|
||||||
38
internal/repository/branch_stats.go
Normal file
38
internal/repository/branch_stats.go
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for creating new branch stats store
|
||||||
|
func NewBranchStatStore(s *Store) ports.BranchStatStore { return s }
|
||||||
|
|
||||||
|
func (s *Store) UpdateBranchStats(ctx context.Context) error {
|
||||||
|
return s.queries.UpdateBranchStats(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) {
|
||||||
|
stats, err := s.queries.GetBranchStatsByID(ctx, branchID)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return domain.ConvertDBBranchStatsList(stats), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) {
|
||||||
|
stats, err := s.queries.GetBranchStats(ctx, dbgen.GetBranchStatsParams{
|
||||||
|
Interval: filter.Interval.ToPG(),
|
||||||
|
BranchID: filter.BranchID.ToPG(),
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.ConvertDBBranchStatsByIntervalList(stats), nil
|
||||||
|
}
|
||||||
37
internal/repository/wallet_stats.go
Normal file
37
internal/repository/wallet_stats.go
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
dbgen "github.com/SamuelTariku/FortuneBet-Backend/gen/db"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Interface for creating new wallet stats store
|
||||||
|
func NewWalletStatStore(s *Store) ports.WalletStatStore { return s }
|
||||||
|
|
||||||
|
func (s *Store) UpdateWalletStats(ctx context.Context) error {
|
||||||
|
return s.queries.UpdateWalletStats(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) {
|
||||||
|
stats, err := s.queries.GetWalletStatsByID(ctx, walletID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.ConvertDBWalletStatsList(stats), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Store) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) {
|
||||||
|
stats, err := s.queries.GetWalletStats(ctx, dbgen.GetWalletStatsParams{
|
||||||
|
Interval: filter.Interval.ToPG(),
|
||||||
|
UserID: filter.UserID.ToPG(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.ConvertDBWalletStatsByIntervalList(stats), nil
|
||||||
|
}
|
||||||
95
internal/services/report/bet.go
Normal file
95
internal/services/report/bet.go
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BetIntervalRow struct {
|
||||||
|
Period string `csv:"Period"`
|
||||||
|
TotalBets int64 `csv:"Total Bets"`
|
||||||
|
TotalStake int64 `csv:"Total Stake"`
|
||||||
|
ActiveBets int64 `csv:"Active Bets"`
|
||||||
|
TotalWins int64 `csv:"Total Wins"`
|
||||||
|
TotalLosses int64 `csv:"Total Losses"`
|
||||||
|
WinBalance int64 `csv:"Win Balance"`
|
||||||
|
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||||
|
TotalUnsettledAmount int64 `csv:"Total Unsettled Amount"`
|
||||||
|
TotalShopBets int64 `csv:"Total Shop Bets"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateBetIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||||
|
if request.Metadata.Interval == nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBetIntervalReport] Metadata interval is empty")
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to parse date interval",
|
||||||
|
zap.String("interval", *request.Metadata.Interval),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := s.statService.GetBetStatsByInterval(ctx, domain.BetStatsByIntervalFilter{
|
||||||
|
Interval: domain.ValidDateInterval{
|
||||||
|
Value: interval,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
CompanyID: request.CompanyID,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to fetch bet stats",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("fetching bet stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
|
var headers []string
|
||||||
|
for _, stat := range stats {
|
||||||
|
endDate, err := domain.GetEndDateFromInterval(interval, stat.Date)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBetIntervalReport] Failed to get end date from interval",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
period := fmt.Sprintf("%s to %s",
|
||||||
|
stat.Date.Format("2006-01-02"),
|
||||||
|
endDate.Format("2006-01-02"),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := BetIntervalRow{
|
||||||
|
Period: period,
|
||||||
|
TotalBets: stat.TotalBets,
|
||||||
|
TotalStake: stat.TotalStake,
|
||||||
|
ActiveBets: stat.ActiveBets,
|
||||||
|
TotalWins: stat.TotalWins,
|
||||||
|
TotalLosses: stat.TotalLosses,
|
||||||
|
WinBalance: stat.WinBalance,
|
||||||
|
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: stat.TotalUnsettledAmount,
|
||||||
|
TotalShopBets: stat.TotalShopBets,
|
||||||
|
}
|
||||||
|
|
||||||
|
if headers == nil {
|
||||||
|
headers, _ = StructToCSVRow(r)
|
||||||
|
rows = append(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, row := StructToCSVRow(r)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.WriteCSV(rows, "bet_interval")
|
||||||
|
}
|
||||||
99
internal/services/report/branch.go
Normal file
99
internal/services/report/branch.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type BranchIntervalRow struct {
|
||||||
|
Period string `csv:"Period"`
|
||||||
|
BranchName string `csv:"CompanyName"`
|
||||||
|
CompanyName string `csv:"CompanyName"`
|
||||||
|
TotalBets int64 `csv:"Total Bets"`
|
||||||
|
TotalStake float32 `csv:"Total Stake"`
|
||||||
|
DeductedStake float32 `csv:"Deducted Stake"`
|
||||||
|
TotalCashOut float32 `csv:"Total CashOut"`
|
||||||
|
TotalCashBacks float32 `csv:"Total CashBacks"`
|
||||||
|
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||||
|
TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"`
|
||||||
|
TotalCashiers int64 `csv:"Total Cashiers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateBranchIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||||
|
|
||||||
|
if request.Metadata.Interval == nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBranchIntervalReport] Metadata interval is empty")
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to parse date interval",
|
||||||
|
zap.String("interval", *request.Metadata.Interval),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := s.statService.GetBranchStatsByInterval(ctx, domain.BranchStatFilter{
|
||||||
|
Interval: domain.ValidDateInterval{
|
||||||
|
Value: interval,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
CompanyID: request.CompanyID,
|
||||||
|
BranchID: domain.ConvertInt64Ptr(request.Metadata.BranchID),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to fetch branch stats",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("fetching branch stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
|
var headers []string
|
||||||
|
for _, stat := range stats {
|
||||||
|
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateBranchIntervalReport] Failed to get end date from interval",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
period := fmt.Sprintf("%s to %s",
|
||||||
|
stat.IntervalStart.Format("2006-01-02"),
|
||||||
|
endDate.Format("2006-01-02"),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := BranchIntervalRow{
|
||||||
|
Period: period,
|
||||||
|
BranchName: stat.BranchName,
|
||||||
|
CompanyName: stat.CompanyName,
|
||||||
|
TotalBets: stat.TotalBets,
|
||||||
|
TotalStake: stat.TotalStake.Float32(),
|
||||||
|
DeductedStake: stat.DeductedStake.Float32(),
|
||||||
|
TotalCashOut: stat.TotalCashOut.Float32(),
|
||||||
|
TotalCashBacks: stat.TotalCashBacks.Float32(),
|
||||||
|
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(),
|
||||||
|
TotalCashiers: stat.TotalCashiers,
|
||||||
|
}
|
||||||
|
|
||||||
|
if headers == nil {
|
||||||
|
headers, _ = StructToCSVRow(r)
|
||||||
|
rows = append(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, row := StructToCSVRow(r)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.WriteCSV(rows, "branch_interval")
|
||||||
|
}
|
||||||
111
internal/services/report/company.go
Normal file
111
internal/services/report/company.go
Normal file
|
|
@ -0,0 +1,111 @@
|
||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CompanyIntervalRow struct {
|
||||||
|
Period string `csv:"Period"`
|
||||||
|
CompanyName string `csv:"CompanyName"`
|
||||||
|
TotalBets int64 `csv:"Total Bets"`
|
||||||
|
TotalStake float32 `csv:"Total Stake"`
|
||||||
|
DeductedStake float32 `csv:"Deducted Stake"`
|
||||||
|
TotalCashOut float32 `csv:"Total CashOut"`
|
||||||
|
TotalCashBacks float32 `csv:"Total CashBacks"`
|
||||||
|
NumberOfUnsettled int64 `csv:"Number Of Unsettled"`
|
||||||
|
TotalUnsettledAmount float32 `csv:"Total Unsettled Amount"`
|
||||||
|
TotalAdmins int64 `csv:"Total Admins"`
|
||||||
|
TotalManagers int64 `csv:"Total Managers"`
|
||||||
|
TotalCashiers int64 `csv:"Total Cashiers"`
|
||||||
|
TotalCustomers int64 `csv:"Total Customers"`
|
||||||
|
TotalApprovers int64 `csv:"Total Approvers"`
|
||||||
|
TotalBranches int64 `csv:"Total Branches"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateCompanyIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||||
|
|
||||||
|
// Only a super-admin is allowed to generate this type of report
|
||||||
|
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||||
|
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Unauthorized user report")
|
||||||
|
return "", ErrUnauthorizedUserReport
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Metadata.Interval == nil {
|
||||||
|
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Metadata interval is empty")
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to parse date interval",
|
||||||
|
zap.String("interval", *request.Metadata.Interval),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := s.statService.GetCompanyStatsByInterval(ctx, domain.CompanyStatFilter{
|
||||||
|
Interval: domain.ValidDateInterval{
|
||||||
|
Value: interval,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to fetch company stats",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("fetching company stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
|
var headers []string
|
||||||
|
for _, stat := range stats {
|
||||||
|
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateCompanyIntervalReport] Failed to get end date from interval",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
period := fmt.Sprintf("%s to %s",
|
||||||
|
stat.IntervalStart.Format("2006-01-02"),
|
||||||
|
endDate.Format("2006-01-02"),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := CompanyIntervalRow{
|
||||||
|
Period: period,
|
||||||
|
CompanyName: stat.CompanyName,
|
||||||
|
TotalBets: stat.TotalBets,
|
||||||
|
TotalStake: stat.TotalStake.Float32(),
|
||||||
|
DeductedStake: stat.DeductedStake.Float32(),
|
||||||
|
TotalCashOut: stat.TotalCashOut.Float32(),
|
||||||
|
TotalCashBacks: stat.TotalCashBacks.Float32(),
|
||||||
|
NumberOfUnsettled: stat.NumberOfUnsettled,
|
||||||
|
TotalUnsettledAmount: stat.TotalUnsettledAmount.Float32(),
|
||||||
|
TotalAdmins: stat.TotalAdmins,
|
||||||
|
TotalManagers: stat.TotalManagers,
|
||||||
|
TotalCashiers: stat.TotalCashiers,
|
||||||
|
TotalCustomers: stat.TotalCustomers,
|
||||||
|
TotalApprovers: stat.TotalApprovers,
|
||||||
|
TotalBranches: stat.TotalBranches,
|
||||||
|
}
|
||||||
|
|
||||||
|
if headers == nil {
|
||||||
|
headers, _ = StructToCSVRow(r)
|
||||||
|
rows = append(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, row := StructToCSVRow(r)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.WriteCSV(rows, "company_interval")
|
||||||
|
}
|
||||||
|
|
@ -8,12 +8,14 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrReportFileNotFound = errors.New("failed to find report file")
|
ErrReportFileNotFound = errors.New("failed to find report file")
|
||||||
ErrReportFileError = errors.New("unknown error with report file")
|
ErrReportFileError = errors.New("unknown error with report file")
|
||||||
|
|
@ -21,6 +23,20 @@ var (
|
||||||
ErrReportFilePathInvalid = errors.New("report file path is invalid")
|
ErrReportFilePathInvalid = errors.New("report file path is invalid")
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func StructToCSVRow(v any) ([]string, []string) {
|
||||||
|
t := reflect.TypeOf(v)
|
||||||
|
val := reflect.ValueOf(v)
|
||||||
|
headers := make([]string, t.NumField())
|
||||||
|
row := make([]string, t.NumField())
|
||||||
|
|
||||||
|
for i := 0; i < t.NumField(); i++ {
|
||||||
|
field := t.Field(i)
|
||||||
|
headers[i] = field.Tag.Get("csv")
|
||||||
|
row[i] = fmt.Sprint(val.Field(i).Interface())
|
||||||
|
}
|
||||||
|
return headers, row
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) WriteCSV(rows [][]string, filePrefix string) (string, error) {
|
func (s *Service) WriteCSV(rows [][]string, filePrefix string) (string, error) {
|
||||||
if len(rows) == 0 {
|
if len(rows) == 0 {
|
||||||
s.mongoLogger.Error("[WriteCSV] CSV with no data",
|
s.mongoLogger.Error("[WriteCSV] CSV with no data",
|
||||||
|
|
|
||||||
|
|
@ -3,21 +3,44 @@ package report
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
type EventIntervalRow struct {
|
||||||
ErrInvalidInterval = errors.New("invalid interval provided")
|
Period string `csv:"Period"`
|
||||||
)
|
TotalEvents int64 `csv:"Total Events"`
|
||||||
|
ActiveEvents int64 `csv:"Active Events"`
|
||||||
|
InActiveEvents int64 `csv:"In-Active Events"`
|
||||||
|
FeaturedEvents int64 `csv:"Featured Events"`
|
||||||
|
Leagues int64 `csv:"Leagues"`
|
||||||
|
Pending int64 `csv:"Pending"`
|
||||||
|
InPlay int64 `csv:"In-Play"`
|
||||||
|
ToBeFixed int64 `csv:"To-Be-Fixed"`
|
||||||
|
Ended int64 `csv:"Ended"`
|
||||||
|
Postponed int64 `csv:"Postponed"`
|
||||||
|
Cancelled int64 `csv:"Cancelled"`
|
||||||
|
Walkover int64 `csv:"Walkover"`
|
||||||
|
Interrupted int64 `csv:"Interrupted"`
|
||||||
|
Abandoned int64 `csv:"Abandoned"`
|
||||||
|
Retired int64 `csv:"Retired"`
|
||||||
|
Suspended int64 `csv:"Suspended"`
|
||||||
|
DecidedByFA int64 `csv:"Decided-By-FA"`
|
||||||
|
Removed int64 `csv:"Removed"`
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||||
|
// Only a super-admin is allowed to generate this type of report
|
||||||
|
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||||
|
s.mongoLogger.Error("[GenerateEventIntervalReport] Unauthorized user report")
|
||||||
|
return "", ErrUnauthorizedUserReport
|
||||||
|
}
|
||||||
|
|
||||||
if request.Metadata.Interval == nil {
|
if request.Metadata.Interval == nil {
|
||||||
s.mongoLogger.Error("[GenerateEventIntervalReport] Metadata interval is empty")
|
s.mongoLogger.Error("[GenerateEventIntervalReport] Metadata interval is empty")
|
||||||
return "", ErrInvalidInterval
|
return "", domain.ErrInvalidInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||||
|
|
@ -26,7 +49,7 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
||||||
zap.String("interval", *request.Metadata.Interval),
|
zap.String("interval", *request.Metadata.Interval),
|
||||||
zap.Error(err),
|
zap.Error(err),
|
||||||
)
|
)
|
||||||
return "", ErrInvalidInterval
|
return "", domain.ErrInvalidInterval
|
||||||
}
|
}
|
||||||
|
|
||||||
stats, err := s.statService.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{
|
stats, err := s.statService.GetTotalEventStatsByInterval(ctx, domain.EventStatsByIntervalFilter{
|
||||||
|
|
@ -43,12 +66,8 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
||||||
return "", fmt.Errorf("fetching event stats: %w", err)
|
return "", fmt.Errorf("fetching event stats: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
rows := [][]string{{
|
var rows [][]string
|
||||||
"Period", "Total Events", "Active Events", "In-Active Events", "Featured Events", "Leagues",
|
var headers []string
|
||||||
"Pending", "In-Play", "To-Be-Fixed", "Ended", "Postponed", "Cancelled", "Walkover",
|
|
||||||
"Interrupted", "Abandoned", "Retired", "Suspended", "Decided-By-FA", "Removed",
|
|
||||||
}}
|
|
||||||
|
|
||||||
for _, stat := range stats {
|
for _, stat := range stats {
|
||||||
endDate, err := domain.GetEndDateFromInterval(interval, stat.Date)
|
endDate, err := domain.GetEndDateFromInterval(interval, stat.Date)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -64,27 +83,35 @@ func (s *Service) GenerateEventIntervalReport(ctx context.Context, request domai
|
||||||
endDate.Format("2006-01-02"),
|
endDate.Format("2006-01-02"),
|
||||||
)
|
)
|
||||||
|
|
||||||
rows = append(rows, []string{
|
r := EventIntervalRow{
|
||||||
period,
|
Period: period,
|
||||||
fmt.Sprint(stat.EventCount),
|
TotalEvents: stat.EventCount,
|
||||||
fmt.Sprint(stat.TotalActiveEvents),
|
ActiveEvents: stat.TotalActiveEvents,
|
||||||
fmt.Sprint(stat.TotalInActiveEvents),
|
InActiveEvents: stat.TotalInActiveEvents,
|
||||||
fmt.Sprint(stat.TotalFeaturedEvents),
|
FeaturedEvents: stat.TotalFeaturedEvents,
|
||||||
fmt.Sprint(stat.TotalLeagues),
|
Leagues: stat.TotalLeagues,
|
||||||
fmt.Sprint(stat.Pending),
|
Pending: stat.Pending,
|
||||||
fmt.Sprint(stat.InPlay),
|
InPlay: stat.InPlay,
|
||||||
fmt.Sprint(stat.ToBeFixed),
|
ToBeFixed: stat.ToBeFixed,
|
||||||
fmt.Sprint(stat.Ended),
|
Ended: stat.Ended,
|
||||||
fmt.Sprint(stat.Postponed),
|
Postponed: stat.Postponed,
|
||||||
fmt.Sprint(stat.Cancelled),
|
Cancelled: stat.Cancelled,
|
||||||
fmt.Sprint(stat.Walkover),
|
Walkover: stat.Walkover,
|
||||||
fmt.Sprint(stat.Interrupted),
|
Interrupted: stat.Interrupted,
|
||||||
fmt.Sprint(stat.Abandoned),
|
Abandoned: stat.Abandoned,
|
||||||
fmt.Sprint(stat.Retired),
|
Retired: stat.Retired,
|
||||||
fmt.Sprint(stat.Suspended),
|
Suspended: stat.Suspended,
|
||||||
fmt.Sprint(stat.DecidedByFa),
|
DecidedByFA: stat.DecidedByFa,
|
||||||
fmt.Sprint(stat.Removed),
|
Removed: stat.Removed,
|
||||||
})
|
}
|
||||||
|
|
||||||
|
if headers == nil {
|
||||||
|
headers, _ = StructToCSVRow(r)
|
||||||
|
rows = append(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, row := StructToCSVRow(r)
|
||||||
|
rows = append(rows, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
return s.WriteCSV(rows, "event_interval")
|
return s.WriteCSV(rows, "event_interval")
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package report
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
|
@ -9,6 +10,11 @@ import (
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrUnauthorizedUserReport = errors.New("unauthorized user report")
|
||||||
|
)
|
||||||
|
|
||||||
func (s *Service) ProcessReportRequests(ctx context.Context) error {
|
func (s *Service) ProcessReportRequests(ctx context.Context) error {
|
||||||
requests, total, err := s.GetAllReportRequests(ctx, domain.ReportRequestFilter{
|
requests, total, err := s.GetAllReportRequests(ctx, domain.ReportRequestFilter{
|
||||||
Status: domain.ValidReportRequestStatus{
|
Status: domain.ValidReportRequestStatus{
|
||||||
|
|
|
||||||
|
|
@ -95,9 +95,11 @@ type ReportGeneratorFunc func(ctx context.Context, req domain.ReportRequestDetai
|
||||||
|
|
||||||
func (s *Service) registerGenerators() {
|
func (s *Service) registerGenerators() {
|
||||||
s.generators = map[domain.ReportRequestType]ReportGeneratorFunc{
|
s.generators = map[domain.ReportRequestType]ReportGeneratorFunc{
|
||||||
domain.EventIntervalReportRequest: s.GenerateEventIntervalReport,
|
domain.EventIntervalReportRequest: s.GenerateEventIntervalReport,
|
||||||
// domain.CompanySummaryReportRequest: s.GenerateCompanySummaryReport,
|
domain.CompanyIntervalReportRequest: s.GenerateCompanyIntervalReport,
|
||||||
// domain.BranchPerformanceReportRequest: s.GenerateBranchPerformanceReport,
|
domain.BranchIntervalReportRequest: s.GenerateBranchIntervalReport,
|
||||||
|
domain.BetIntervalReportRequest: s.GenerateBetIntervalReport,
|
||||||
|
domain.WalletIntervalReportRequest: s.GenerateWalletIntervalReport,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
107
internal/services/report/wallet.go
Normal file
107
internal/services/report/wallet.go
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
package report
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WalletIntervalRow struct {
|
||||||
|
Period string `csv:"Period"`
|
||||||
|
WalletUserFirstName string `csv:"User First Name"`
|
||||||
|
WalletUserLastName string `csv:"User Last Name"`
|
||||||
|
WalletID int64 `csv:"Wallet Id"`
|
||||||
|
WalletType string `csv:"Wallet Type"`
|
||||||
|
NumberOfTransactions int64 `csv:"Number Of Transactions"`
|
||||||
|
TotalTransactions float32 `csv:"Total Transactions"`
|
||||||
|
NumberOfDeposits int64 `csv:"Number Of Deposits"`
|
||||||
|
TotalDepositsAmount float32 `csv:"Total Deposits Amount"`
|
||||||
|
NumberOfWithdraws int64 `csv:"Number Of Withdraws"`
|
||||||
|
TotalWithdrawsAmount float32 `csv:"Total Withdraws Amount"`
|
||||||
|
NumberOfTransfers int64 `csv:"Number Of Transfers"`
|
||||||
|
TotalTransfersAmount float32 `csv:"Total Transfers Amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) GenerateWalletIntervalReport(ctx context.Context, request domain.ReportRequestDetail) (string, error) {
|
||||||
|
|
||||||
|
// Only a super-admin is allowed to generate this type of report
|
||||||
|
if request.RequesterRole.Valid && request.RequesterRole.Value != domain.RoleSuperAdmin {
|
||||||
|
s.mongoLogger.Error("[GenerateWalletIntervalReport] Unauthorized user report")
|
||||||
|
return "", ErrUnauthorizedUserReport
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Metadata.Interval == nil {
|
||||||
|
s.mongoLogger.Error("[GenerateWalletIntervalReport] Metadata interval is empty")
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
interval, err := domain.ParseDateInterval(*request.Metadata.Interval)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to parse date interval",
|
||||||
|
zap.String("interval", *request.Metadata.Interval),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", domain.ErrInvalidInterval
|
||||||
|
}
|
||||||
|
|
||||||
|
stats, err := s.statService.GetWalletStatsByInterval(ctx, domain.WalletStatFilter{
|
||||||
|
Interval: domain.ValidDateInterval{
|
||||||
|
Value: interval,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to fetch wallet stats",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("fetching wallet stats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var rows [][]string
|
||||||
|
var headers []string
|
||||||
|
for _, stat := range stats {
|
||||||
|
endDate, err := domain.GetEndDateFromInterval(interval, stat.IntervalStart)
|
||||||
|
if err != nil {
|
||||||
|
s.mongoLogger.Error("[GenerateWalletIntervalReport] Failed to get end date from interval",
|
||||||
|
zap.String("interval", string(interval)),
|
||||||
|
zap.Error(err),
|
||||||
|
)
|
||||||
|
return "", fmt.Errorf("invalid interval end date: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
period := fmt.Sprintf("%s to %s",
|
||||||
|
stat.IntervalStart.Format("2006-01-02"),
|
||||||
|
endDate.Format("2006-01-02"),
|
||||||
|
)
|
||||||
|
|
||||||
|
r := WalletIntervalRow{
|
||||||
|
Period: period,
|
||||||
|
WalletUserFirstName: stat.WalletUserFirstName,
|
||||||
|
WalletUserLastName: stat.WalletUserLastName,
|
||||||
|
WalletID: stat.WalletID,
|
||||||
|
WalletType: stat.WalletType,
|
||||||
|
NumberOfTransactions: stat.NumberOfTransactions,
|
||||||
|
TotalTransactions: stat.TotalTransactions.Float32(),
|
||||||
|
NumberOfDeposits: stat.NumberOfDeposits,
|
||||||
|
TotalDepositsAmount: stat.TotalDepositsAmount.Float32(),
|
||||||
|
NumberOfWithdraws: stat.NumberOfWithdraws,
|
||||||
|
TotalWithdrawsAmount: stat.TotalWithdrawsAmount.Float32(),
|
||||||
|
NumberOfTransfers: stat.NumberOfTransfers,
|
||||||
|
TotalTransfersAmount: stat.TotalTransfersAmount.Float32(),
|
||||||
|
}
|
||||||
|
|
||||||
|
if headers == nil {
|
||||||
|
headers, _ = StructToCSVRow(r)
|
||||||
|
rows = append(rows, headers)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, row := StructToCSVRow(r)
|
||||||
|
rows = append(rows, row)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.WriteCSV(rows, "wallet_interval")
|
||||||
|
}
|
||||||
11
internal/services/stats/bet.go
Normal file
11
internal/services/stats/bet.go
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) GetBetStatsByInterval(ctx context.Context, filter domain.BetStatsByIntervalFilter) ([]domain.BetStatsByInterval, error) {
|
||||||
|
return s.betStatStore.GetBetStatsByInterval(ctx, filter)
|
||||||
|
}
|
||||||
16
internal/services/stats/branch.go
Normal file
16
internal/services/stats/branch.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) UpdateBranchStats(ctx context.Context) error {
|
||||||
|
return s.branchStatStore.UpdateBranchStats(ctx)
|
||||||
|
}
|
||||||
|
func (s *Service) GetBranchStatByID(ctx context.Context, branchID int64) ([]domain.BranchStat, error) {
|
||||||
|
return s.branchStatStore.GetBranchStatByID(ctx, branchID)
|
||||||
|
}
|
||||||
|
func (s *Service) GetBranchStatsByInterval(ctx context.Context, filter domain.BranchStatFilter) ([]domain.BranchStat, error) {
|
||||||
|
return s.branchStatStore.GetBranchStatsByInterval(ctx, filter)
|
||||||
|
}
|
||||||
|
|
@ -6,11 +6,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *Service) UpdateCompanyStats(ctx context.Context) error {
|
func (s *Service) UpdateCompanyStats(ctx context.Context) error {
|
||||||
return s.UpdateCompanyStats(ctx)
|
return s.companyStatStore.UpdateCompanyStats(ctx)
|
||||||
}
|
}
|
||||||
func (s *Service) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) {
|
func (s *Service) GetCompanyStatByID(ctx context.Context, companyID int64) ([]domain.CompanyStat, error) {
|
||||||
return s.GetCompanyStatByID(ctx, companyID)
|
return s.companyStatStore.GetCompanyStatByID(ctx, companyID)
|
||||||
}
|
}
|
||||||
func (s *Service) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) {
|
func (s *Service) GetCompanyStatsByInterval(ctx context.Context, filter domain.CompanyStatFilter) ([]domain.CompanyStat, error) {
|
||||||
return s.GetCompanyStatsByInterval(ctx, filter)
|
return s.companyStatStore.GetCompanyStatsByInterval(ctx, filter)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,12 +4,24 @@ import "github.com/SamuelTariku/FortuneBet-Backend/internal/ports"
|
||||||
|
|
||||||
type Service struct {
|
type Service struct {
|
||||||
companyStatStore ports.CompanyStatStore
|
companyStatStore ports.CompanyStatStore
|
||||||
eventStatStore ports.EventStatStore
|
branchStatStore ports.BranchStatStore
|
||||||
|
eventStatStore ports.EventStatStore
|
||||||
|
betStatStore ports.BetStatStore
|
||||||
|
walletStatStore ports.WalletStatStore
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(companyStatStore ports.CompanyStatStore, eventStatStore ports.EventStatStore) *Service {
|
func NewService(
|
||||||
|
companyStatStore ports.CompanyStatStore,
|
||||||
|
branchStatStore ports.BranchStatStore,
|
||||||
|
eventStatStore ports.EventStatStore,
|
||||||
|
betStatStore ports.BetStatStore,
|
||||||
|
walletStatStore ports.WalletStatStore,
|
||||||
|
) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
companyStatStore: companyStatStore,
|
companyStatStore: companyStatStore,
|
||||||
eventStatStore: eventStatStore,
|
branchStatStore: branchStatStore,
|
||||||
|
eventStatStore: eventStatStore,
|
||||||
|
betStatStore: betStatStore,
|
||||||
|
walletStatStore: walletStatStore,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
internal/services/stats/wallet.go
Normal file
16
internal/services/stats/wallet.go
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
package stats
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (s *Service) UpdateWalletStats(ctx context.Context) error {
|
||||||
|
return s.walletStatStore.UpdateWalletStats(ctx)
|
||||||
|
}
|
||||||
|
func (s *Service) GetWalletStatByID(ctx context.Context, walletID int64) ([]domain.WalletStat, error) {
|
||||||
|
return s.walletStatStore.GetWalletStatByID(ctx, walletID)
|
||||||
|
}
|
||||||
|
func (s *Service) GetWalletStatsByInterval(ctx context.Context, filter domain.WalletStatFilter) ([]domain.WalletStat, error) {
|
||||||
|
return s.walletStatStore.GetWalletStatsByInterval(ctx, filter)
|
||||||
|
}
|
||||||
|
|
@ -235,6 +235,44 @@ func StartStatCrons(statService *stats.Service, mongoLogger *zap.Logger) {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
spec: "0 0 * * * *", // Every hour
|
||||||
|
task: func() {
|
||||||
|
start := time.Now()
|
||||||
|
mongoLogger.Info("[Branch Stats Crons] Updating branch stats", zap.Time("timestamp", time.Now()))
|
||||||
|
if err := statService.UpdateBranchStats(context.Background()); err != nil {
|
||||||
|
mongoLogger.Error("[Branch Stats Crons] Failed to update branch stats",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Time("timestamp", time.Now()),
|
||||||
|
zap.Duration("duration", time.Since(start)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mongoLogger.Info("[Branch Stats Crons] Successfully updated branch stats",
|
||||||
|
zap.Time("timestamp", time.Now()),
|
||||||
|
zap.Duration("duration", time.Since(start)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
spec: "0 0 * * * *", // Every hour
|
||||||
|
task: func() {
|
||||||
|
start := time.Now()
|
||||||
|
mongoLogger.Info("[Wallet Stats Crons] Updating wallet stats", zap.Time("timestamp", time.Now()))
|
||||||
|
if err := statService.UpdateWalletStats(context.Background()); err != nil {
|
||||||
|
mongoLogger.Error("[Wallet Stats Crons] Failed to update wallet stats",
|
||||||
|
zap.Error(err),
|
||||||
|
zap.Time("timestamp", time.Now()),
|
||||||
|
zap.Duration("duration", time.Since(start)),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
mongoLogger.Info("[Wallet Stats Crons] Successfully updated wallet stats",
|
||||||
|
zap.Time("timestamp", time.Now()),
|
||||||
|
zap.Duration("duration", time.Since(start)),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
spec: "0 0 * * * *", // Hourly
|
spec: "0 0 * * * *", // Hourly
|
||||||
task: func() {
|
task: func() {
|
||||||
|
|
|
||||||
|
|
@ -38,38 +38,56 @@ func convertWallet(wallet domain.Wallet) WalletRes {
|
||||||
}
|
}
|
||||||
|
|
||||||
type CustomerWalletRes struct {
|
type CustomerWalletRes struct {
|
||||||
ID int64 `json:"id" example:"1"`
|
ID int64 `json:"id" example:"1"`
|
||||||
RegularID int64 `json:"regular_id" example:"1"`
|
RegularID int64 `json:"regular_id" example:"1"`
|
||||||
RegularBalance float32 `json:"regular_balance" example:"100.0"`
|
RegularBalance float32 `json:"regular_balance" example:"100.0"`
|
||||||
StaticID int64 `json:"static_id" example:"1"`
|
StaticID int64 `json:"static_id" example:"1"`
|
||||||
StaticBalance float32 `json:"static_balance" example:"100.0"`
|
StaticBalance float32 `json:"static_balance" example:"100.0"`
|
||||||
CustomerID int64 `json:"customer_id" example:"1"`
|
CustomerID int64 `json:"customer_id" example:"1"`
|
||||||
RegularIsActive bool `json:"regular_is_active" example:"true"`
|
RegularIsActive bool `json:"regular_is_active" example:"true"`
|
||||||
StaticIsActive bool `json:"static_is_active" example:"true"`
|
StaticIsActive bool `json:"static_is_active" example:"true"`
|
||||||
RegularUpdatedAt time.Time `json:"regular_updated_at"`
|
RegularUpdatedAt time.Time `json:"regular_updated_at"`
|
||||||
StaticUpdatedAt time.Time `json:"static_updated_at"`
|
StaticUpdatedAt time.Time `json:"static_updated_at"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
FirstName string `json:"first_name" example:"John"`
|
FirstName string `json:"first_name" example:"John"`
|
||||||
LastName string `json:"last_name" example:"Smith"`
|
LastName string `json:"last_name" example:"Smith"`
|
||||||
PhoneNumber string `json:"phone_number" example:"0911111111"`
|
PhoneNumber string `json:"phone_number" example:"0911111111"`
|
||||||
|
NumberOfTransactions int64 `json:"number_of_transactions"`
|
||||||
|
TotalTransactions float32 `json:"total_transactions"`
|
||||||
|
NumberOfDeposits int64 `json:"number_of_deposits"`
|
||||||
|
TotalDepositsAmount float32 `json:"total_deposits_amount"`
|
||||||
|
NumberOfWithdraws int64 `json:"number_of_withdraws"`
|
||||||
|
TotalWithdrawsAmount float32 `json:"total_withdraws_amount"`
|
||||||
|
NumberOfTransfers int64 `json:"number_of_transfers"`
|
||||||
|
TotalTransfersAmount float32 `json:"total_transfers_amount"`
|
||||||
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
|
func ConvertCustomerWallet(wallet domain.GetCustomerWallet) CustomerWalletRes {
|
||||||
return CustomerWalletRes{
|
return CustomerWalletRes{
|
||||||
ID: wallet.ID,
|
ID: wallet.ID,
|
||||||
RegularID: wallet.RegularID,
|
RegularID: wallet.RegularID,
|
||||||
RegularBalance: wallet.RegularBalance.Float32(),
|
RegularBalance: wallet.RegularBalance.Float32(),
|
||||||
StaticID: wallet.StaticID,
|
StaticID: wallet.StaticID,
|
||||||
StaticBalance: wallet.StaticBalance.Float32(),
|
StaticBalance: wallet.StaticBalance.Float32(),
|
||||||
CustomerID: wallet.CustomerID,
|
CustomerID: wallet.CustomerID,
|
||||||
RegularIsActive: wallet.RegularIsActive,
|
RegularIsActive: wallet.RegularIsActive,
|
||||||
StaticIsActive: wallet.StaticIsActive,
|
StaticIsActive: wallet.StaticIsActive,
|
||||||
RegularUpdatedAt: wallet.RegularUpdatedAt,
|
RegularUpdatedAt: wallet.RegularUpdatedAt,
|
||||||
StaticUpdatedAt: wallet.StaticUpdatedAt,
|
StaticUpdatedAt: wallet.StaticUpdatedAt,
|
||||||
CreatedAt: wallet.CreatedAt,
|
CreatedAt: wallet.CreatedAt,
|
||||||
FirstName: wallet.FirstName,
|
FirstName: wallet.FirstName,
|
||||||
LastName: wallet.LastName,
|
LastName: wallet.LastName,
|
||||||
PhoneNumber: wallet.PhoneNumber,
|
PhoneNumber: wallet.PhoneNumber,
|
||||||
|
NumberOfTransactions: wallet.NumberOfTransactions,
|
||||||
|
TotalTransactions: wallet.TotalTransactions.Float32(),
|
||||||
|
NumberOfDeposits: wallet.NumberOfDeposits,
|
||||||
|
TotalDepositsAmount: wallet.TotalDepositsAmount.Float32(),
|
||||||
|
NumberOfWithdraws: wallet.NumberOfWithdraws,
|
||||||
|
TotalWithdrawsAmount: wallet.TotalWithdrawsAmount.Float32(),
|
||||||
|
NumberOfTransfers: wallet.NumberOfTransfers,
|
||||||
|
TotalTransfersAmount: wallet.TotalTransfersAmount.Float32(),
|
||||||
|
UpdatedAt: wallet.UpdatedAt,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user