mirror of
https://github.com/Dvorinka/Bookra.git
synced 2026-06-04 20:43:01 +00:00
feat(sms): implement SMS messaging and metered billing
Implement a complete SMS messaging system including: - Integration with SMS Manager.cz API for sending messages. - Metered billing via Stripe using monthly aggregate invoice items. - Backend services for managing SMS settings, usage logging, and monthly reporting. - Database migrations for tenant settings, usage logs, and monthly reports. - Frontend dashboard components for SMS configuration, usage tracking, and history. - Support for customer phone numbers in the booking flow. Includes new migrations, backend services, and frontend UI components.
This commit is contained in:
@@ -86,6 +86,18 @@ type Repository interface {
|
||||
// Working Hours
|
||||
ListWorkingHoursByTenant(ctx context.Context, tenantID string) ([]WorkingHoursRecord, error)
|
||||
UpdateWorkingHours(ctx context.Context, tenantID string, dayOfWeek int, params UpdateWorkingHoursParams) error
|
||||
|
||||
// SMS
|
||||
GetTenantSMSSettings(ctx context.Context, tenantID string) (TenantSMSSettingsRecord, error)
|
||||
UpsertTenantSMSSettings(ctx context.Context, params TenantSMSSettingsRecord) error
|
||||
CreateSMSUsageLog(ctx context.Context, params SMSUsageLogRecord) (string, error)
|
||||
GetSMSUsageThisMonth(ctx context.Context, tenantID string) (SMSUsageSummary, error)
|
||||
GetSMSUsageForMonth(ctx context.Context, tenantID string, yearMonth string) (SMSMonthlyReportRecord, error)
|
||||
ListSMSUsageLogs(ctx context.Context, tenantID string, limit int) ([]SMSUsageLogRecord, error)
|
||||
ListSMSMonthlyReports(ctx context.Context, tenantID string, limit int) ([]SMSMonthlyReportRecord, error)
|
||||
UpsertSMSMonthlyReport(ctx context.Context, params SMSMonthlyReportRecord) error
|
||||
MarkSMSReportInvoiceSent(ctx context.Context, tenantID string, yearMonth string) error
|
||||
ListTenantsWithSMSUsage(ctx context.Context, yearMonth string) ([]TenantRecord, error)
|
||||
}
|
||||
|
||||
type TenantRecord struct {
|
||||
@@ -124,12 +136,12 @@ type MagicLinkRecord struct {
|
||||
}
|
||||
|
||||
type PlatformStats struct {
|
||||
TotalTenants int64 `json:"totalTenants"`
|
||||
TotalUsers int64 `json:"totalUsers"`
|
||||
TotalTenants int64 `json:"totalTenants"`
|
||||
TotalUsers int64 `json:"totalUsers"`
|
||||
ActiveSubscriptions int64 `json:"activeSubscriptions"`
|
||||
TrialSubscriptions int64 `json:"trialSubscriptions"`
|
||||
BookingsThisMonth int64 `json:"bookingsThisMonth"`
|
||||
RevenueThisMonth int64 `json:"revenueThisMonthCents"`
|
||||
BookingsThisMonth int64 `json:"bookingsThisMonth"`
|
||||
RevenueThisMonth int64 `json:"revenueThisMonthCents"`
|
||||
}
|
||||
|
||||
type AdminAuditLogParams struct {
|
||||
@@ -229,6 +241,7 @@ type BookingRecord struct {
|
||||
LocationID *string
|
||||
CustomerName string
|
||||
CustomerEmail string
|
||||
CustomerPhone string
|
||||
StartsAt time.Time
|
||||
EndsAt time.Time
|
||||
Status string
|
||||
@@ -244,6 +257,7 @@ type CreateBookingParams struct {
|
||||
BookingMode string
|
||||
CustomerName string
|
||||
CustomerEmail string
|
||||
CustomerPhone string
|
||||
StartsAt time.Time
|
||||
EndsAt time.Time
|
||||
Status string
|
||||
@@ -414,6 +428,44 @@ type UpdateWorkingHoursParams struct {
|
||||
IsOpen *bool
|
||||
}
|
||||
|
||||
// SMS Records
|
||||
type TenantSMSSettingsRecord struct {
|
||||
TenantID string
|
||||
Enabled bool
|
||||
SenderName string
|
||||
MonthlyLimit int
|
||||
StripeSubscriptionItemID string
|
||||
}
|
||||
|
||||
type SMSUsageLogRecord struct {
|
||||
ID string
|
||||
TenantID string
|
||||
RecipientPhone string
|
||||
MessageBody string
|
||||
ExternalMessageID string
|
||||
ExternalRequestID string
|
||||
Status string
|
||||
CostCents int
|
||||
SentAt time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type SMSUsageSummary struct {
|
||||
MessageCount int
|
||||
TotalCostCents int
|
||||
}
|
||||
|
||||
type SMSMonthlyReportRecord struct {
|
||||
ID string
|
||||
TenantID string
|
||||
YearMonth string
|
||||
MessageCount int
|
||||
TotalCostCents int
|
||||
StripeInvoiceID string
|
||||
InvoiceSentAt *time.Time
|
||||
CreatedAt time.Time
|
||||
}
|
||||
|
||||
type PGRepository struct {
|
||||
pool *pgxpool.Pool
|
||||
}
|
||||
@@ -428,46 +480,14 @@ func NewRepository(pools *Pools, demoMode bool) Repository {
|
||||
return NewMemoryRepository()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================================
|
||||
// LOCATION / ZONE METHODS - PG REPOSITORY (STUBS)
|
||||
// ============================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================================
|
||||
// BLOCKED DAYS METHODS - PG REPOSITORY (STUBS)
|
||||
// ============================================
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// ============================================
|
||||
// CUSTOMER METHODS - PG REPOSITORY (STUBS)
|
||||
// ============================================
|
||||
@@ -572,11 +592,11 @@ func (r *PGRepository) GetBookingByReference(ctx context.Context, reference stri
|
||||
var rec BookingRecord
|
||||
err := r.pool.QueryRow(ctx, `
|
||||
SELECT id, tenant_id, service_id, class_session_id, staff_id, location_id,
|
||||
customer_name, customer_email, starts_at, ends_at, status, reference
|
||||
customer_name, customer_email, customer_phone, starts_at, ends_at, status, reference
|
||||
FROM bookings
|
||||
WHERE reference = $1
|
||||
`, reference).Scan(&rec.ID, &rec.TenantID, &rec.ServiceID, &rec.ClassSessionID, &rec.StaffID, &rec.LocationID,
|
||||
&rec.CustomerName, &rec.CustomerEmail, &rec.StartsAt, &rec.EndsAt, &rec.Status, &rec.Reference)
|
||||
&rec.CustomerName, &rec.CustomerEmail, &rec.CustomerPhone, &rec.StartsAt, &rec.EndsAt, &rec.Status, &rec.Reference)
|
||||
return rec, err
|
||||
}
|
||||
|
||||
@@ -598,8 +618,6 @@ func (r *PGRepository) RescheduleBooking(ctx context.Context, bookingID string,
|
||||
// WORKING HOURS METHODS - PG REPOSITORY (STUBS)
|
||||
// ============================================
|
||||
|
||||
|
||||
|
||||
type MemoryRepository struct {
|
||||
tenant TenantRecord
|
||||
membership TenantMembershipRecord
|
||||
@@ -617,6 +635,9 @@ type MemoryRepository struct {
|
||||
blockedDays []BlockedDayRecord
|
||||
customers []CustomerRecord
|
||||
workingHours []WorkingHoursRecord
|
||||
smsSettings TenantSMSSettingsRecord
|
||||
smsLogs []SMSUsageLogRecord
|
||||
smsReports []SMSMonthlyReportRecord
|
||||
}
|
||||
|
||||
func NewMemoryRepository() *MemoryRepository {
|
||||
|
||||
Reference in New Issue
Block a user