saveinmed/backend/internal/domain/models.go

607 lines
24 KiB
Go

package domain
import (
"strings"
"time"
"github.com/gofrs/uuid/v5"
)
// CompanyID and UserID are UUIDv7 identifiers for performance-friendly indexing.
type CompanyID = uuid.UUID
type UserID = uuid.UUID
// Tenant represents a B2B actor (pharmacy/distributor) in the marketplace.
type Tenant struct {
ID CompanyID `db:"id" json:"id"`
CNPJ string `db:"cnpj" json:"cnpj"`
CorporateName string `db:"corporate_name" json:"corporate_name"`
Category string `db:"category" json:"category"` // farmacia, distribuidora
LicenseNumber string `db:"license_number" json:"license_number"`
IsVerified bool `db:"is_verified" json:"is_verified"`
// Location
Latitude float64 `db:"latitude" json:"latitude"`
Longitude float64 `db:"longitude" json:"longitude"`
City string `db:"city" json:"city"`
State string `db:"state" json:"state"`
// Contact & Hours
Phone string `db:"phone" json:"phone"`
OperatingHours string `db:"operating_hours" json:"operating_hours"` // e.g. "Seg-Sex: 08:00-18:00, Sab: 08:00-12:00"
Is24Hours bool `db:"is_24_hours" json:"is_24_hours"`
// Credit Lines (Boleto a Prazo)
CreditLimitCents int64 `db:"credit_limit_cents" json:"credit_limit_cents"` // max credit allowed
CreditUsedCents int64 `db:"credit_used_cents" json:"credit_used_cents"` // currently in use
// Timestamps
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// Company is an alias for Tenant for backward compatibility.
type Company = Tenant
// Role constants
const (
RoleAdmin = "admin"
RoleOwner = "owner"
RoleEmployee = "employee"
RoleDelivery = "delivery"
)
func NormalizeRole(role string) string {
switch strings.ToLower(strings.TrimSpace(role)) {
case "admin", "superadmin", "super_admin", "admin_master":
return RoleAdmin
case "owner", "dono", "seller", "vendedor", "comprador", "gerente", "manager":
return RoleOwner
case "employee", "colaborador":
return RoleEmployee
case "delivery", "entregador":
return RoleDelivery
default:
return RoleOwner
}
}
func IsAdminRole(role string) bool {
return NormalizeRole(role) == RoleAdmin
}
// User represents an authenticated actor inside a company.
type User struct {
ID UserID `db:"id" json:"id"`
CompanyID CompanyID `db:"company_id" json:"company_id"`
Role string `db:"role" json:"role"`
Name string `db:"name" json:"name"`
Username string `db:"username" json:"username"`
Email string `db:"email" json:"email"`
EmailVerified bool `db:"email_verified" json:"email_verified"`
PasswordHash string `db:"password_hash" json:"-"`
Superadmin bool `db:"superadmin" json:"superadmin"`
NomeSocial string `db:"nome_social" json:"nome_social"`
CPF string `db:"cpf" json:"cpf"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
// Rich Data (Populated manually)
Enderecos []Address `db:"-" json:"enderecos"`
EmpresasDados []Company `db:"-" json:"empresas_dados"`
}
// UserFilter captures listing constraints.
type UserFilter struct {
CompanyID *CompanyID
Limit int
Offset int
}
// UserPage wraps paginated results.
type UserPage struct {
Users []User `json:"users"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// Product represents a medicine SKU with batch tracking.
type Product struct {
ID uuid.UUID `db:"id" json:"id"`
SellerID uuid.UUID `db:"seller_id" json:"seller_id"` // Who created this catalog entry (usually Admin/Master)
EANCode string `db:"ean_code" json:"ean_code"`
Name string `db:"name" json:"name"`
Description string `db:"description" json:"description"`
Manufacturer string `db:"manufacturer" json:"manufacturer"`
Category string `db:"category" json:"category"`
Subcategory string `db:"subcategory" json:"subcategory"`
PriceCents int64 `db:"price_cents" json:"price_cents"` // Base/List Price
// Compatibility for frontend
SalePriceCents int64 `db:"price_cents" json:"sale_price_cents,omitempty"`
// New Fields (Reference Data)
InternalCode string `db:"internal_code" json:"internal_code"`
FactoryPriceCents int64 `db:"factory_price_cents" json:"factory_price_cents"`
PMCCents int64 `db:"pmc_cents" json:"pmc_cents"`
CommercialDiscountCents int64 `db:"commercial_discount_cents" json:"commercial_discount_cents"`
TaxSubstitutionCents int64 `db:"tax_substitution_cents" json:"tax_substitution_cents"`
InvoicePriceCents int64 `db:"invoice_price_cents" json:"invoice_price_cents"`
Observations string `db:"observations" json:"observations"`
// Inventory/Batch Fields
Batch string `db:"batch" json:"batch"`
Stock int64 `db:"stock" json:"stock"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// InventoryItem represents a product in a specific seller's stock.
type InventoryItem struct {
ID uuid.UUID `db:"id" json:"id"`
ProductID uuid.UUID `db:"product_id" json:"product_id"` // catalogo_id
SellerID uuid.UUID `db:"seller_id" json:"seller_id"` // empresa_id
SalePriceCents int64 `db:"sale_price_cents" json:"sale_price_cents"` // preco_venda
StockQuantity int64 `db:"stock_quantity" json:"stock_quantity"` // qtdade_estoque
Batch string `db:"batch" json:"batch"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"` // data_validade
Observations string `db:"observations" json:"observations"`
ProductName string `db:"product_name" json:"nome"` // Added for frontend display
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
// Compatibility fields for frontend
Name string `db:"product_name" json:"name,omitempty"`
Quantity int64 `db:"stock_quantity" json:"quantity,omitempty"`
PriceCents int64 `db:"sale_price_cents" json:"price_cents,omitempty"`
EANCode string `db:"ean_code" json:"ean_code,omitempty"`
}
// InventoryFilter allows filtering by expiration window with pagination.
type InventoryFilter struct {
ExpiringBefore *time.Time
SellerID *uuid.UUID
Limit int
Offset int
}
// InventoryPage wraps paginated inventory results.
type InventoryPage struct {
Items []InventoryItem `json:"items"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ProductFilter captures product listing constraints.
type ProductFilter struct {
SellerID *uuid.UUID
Category string
Search string
Limit int
Offset int
}
// ProductPage wraps paginated product results.
type ProductPage struct {
Products []Product `json:"products"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// CompanyFilter captures company/tenant listing constraints.
type CompanyFilter struct {
Category string
Search string
City string
State string
IsVerified *bool
Limit int
Offset int
}
// TenantFilter is an alias for CompanyFilter.
type TenantFilter = CompanyFilter
// CompanyPage wraps paginated company/tenant results.
type CompanyPage struct {
Companies []Company `json:"tenants"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// TenantPage is an alias for CompanyPage.
type TenantPage = CompanyPage
// OrderFilter captures order listing constraints.
type OrderFilter struct {
BuyerID *uuid.UUID
SellerID *uuid.UUID
Status OrderStatus
Limit int
Offset int
}
// OrderPage wraps paginated order results.
type OrderPage struct {
Orders []Order `json:"orders"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// InventoryAdjustment records manual stock corrections.
type InventoryAdjustment struct {
ID uuid.UUID `db:"id" json:"id"`
ProductID uuid.UUID `db:"product_id" json:"product_id"`
Delta int64 `db:"delta" json:"delta"`
Reason string `db:"reason" json:"reason"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
// Order captures the status lifecycle and payment intent.
type Order struct {
ID uuid.UUID `db:"id" json:"id"`
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
SellerID uuid.UUID `db:"seller_id" json:"seller_id"`
Status OrderStatus `db:"status" json:"status"`
TotalCents int64 `db:"total_cents" json:"total_cents"`
PaymentMethod PaymentMethod `db:"payment_method" json:"payment_method"`
ShippingFeeCents int64 `db:"shipping_fee_cents" json:"shipping_fee_cents"` // Adicionado
DistanceKm float64 `db:"distance_km" json:"distance_km"` // Adicionado
Items []OrderItem `json:"items"`
Shipping ShippingAddress `json:"shipping"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// OrderItem stores SKU-level batch tracking.
type OrderItem struct {
ID uuid.UUID `db:"id" json:"id"`
OrderID uuid.UUID `db:"order_id" json:"order_id"`
ProductID uuid.UUID `db:"product_id" json:"product_id"`
Quantity int64 `db:"quantity" json:"quantity"`
UnitCents int64 `db:"unit_cents" json:"unit_cents"`
Batch string `db:"batch" json:"batch"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
}
// PaymentPreference wraps the data needed for Mercado Pago split payments.
type PaymentPreference struct {
OrderID uuid.UUID `json:"order_id"`
Gateway string `json:"gateway"`
PaymentID string `json:"payment_id"`
CommissionPct float64 `json:"commission_pct"`
MarketplaceFee int64 `json:"marketplace_fee"`
SellerReceivable int64 `json:"seller_receivable"`
PaymentURL string `json:"payment_url"`
}
// PaymentWebhookEvent represents Mercado Pago notifications with split amounts.
type PaymentWebhookEvent struct {
PaymentID string `json:"payment_id"`
OrderID uuid.UUID `json:"order_id"`
Status string `json:"status"`
Gateway string `json:"gateway"`
MarketplaceFee int64 `json:"marketplace_fee"`
SellerAmount int64 `json:"seller_amount"`
TotalPaidAmount int64 `json:"total_paid_amount"`
}
// PaymentSplitResult echoes the amounts distributed between actors.
type PaymentSplitResult struct {
OrderID uuid.UUID `json:"order_id"`
PaymentID string `json:"payment_id"`
Status string `json:"status"`
MarketplaceFee int64 `json:"marketplace_fee"`
SellerReceivable int64 `json:"seller_receivable"`
TotalPaidAmount int64 `json:"total_paid_amount"`
}
// PaymentResult represents the result of a payment confirmation.
type PaymentResult struct {
PaymentID string `json:"payment_id"`
Status string `json:"status"`
Gateway string `json:"gateway"`
Message string `json:"message,omitempty"`
ConfirmedAt time.Time `json:"confirmed_at"`
}
// RefundResult represents the result of a refund operation.
type RefundResult struct {
RefundID string `json:"refund_id"`
PaymentID string `json:"payment_id"`
AmountCents int64 `json:"amount_cents"`
Status string `json:"status"`
RefundedAt time.Time `json:"refunded_at"`
}
// PixPaymentResult represents a Pix payment with QR code.
type PixPaymentResult struct {
PaymentID string `json:"payment_id"`
OrderID uuid.UUID `json:"order_id"`
Gateway string `json:"gateway"`
PixKey string `json:"pix_key"`
QRCode string `json:"qr_code"`
QRCodeBase64 string `json:"qr_code_base64"`
CopyPasta string `json:"copy_pasta"`
AmountCents int64 `json:"amount_cents"`
MarketplaceFee int64 `json:"marketplace_fee"`
SellerReceivable int64 `json:"seller_receivable"`
ExpiresAt time.Time `json:"expires_at"`
Status string `json:"status"`
}
// BoletoPaymentResult represents a Boleto payment.
type BoletoPaymentResult struct {
PaymentID string `json:"payment_id"`
OrderID uuid.UUID `json:"order_id"`
Gateway string `json:"gateway"`
BoletoURL string `json:"boleto_url"`
BarCode string `json:"bar_code"`
DigitableLine string `json:"digitable_line"`
AmountCents int64 `json:"amount_cents"`
MarketplaceFee int64 `json:"marketplace_fee"`
SellerReceivable int64 `json:"seller_receivable"`
DueDate time.Time `json:"due_date"`
Status string `json:"status"`
}
// SellerPaymentAccount represents a seller's payment gateway account.
type SellerPaymentAccount struct {
SellerID uuid.UUID `json:"seller_id" db:"seller_id"`
Gateway string `json:"gateway" db:"gateway"`
AccountID string `json:"account_id" db:"account_id"`
AccountType string `json:"account_type" db:"account_type"` // "connect", "subaccount"
Status string `json:"status" db:"status"` // "pending", "active", "suspended"
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// Customer represents a buyer for payment gateway purposes.
type Customer struct {
ID uuid.UUID `json:"id" db:"id"`
Name string `json:"name" db:"name"`
Email string `json:"email" db:"email"`
CPF string `json:"cpf,omitempty" db:"cpf"`
Phone string `json:"phone,omitempty" db:"phone"`
CreatedAt time.Time `json:"created_at" db:"created_at"`
}
// PaymentGatewayConfig stores encrypted gateway credentials.
type PaymentGatewayConfig struct {
Provider string `json:"provider" db:"provider"` // mercadopago, stripe, asaas
Active bool `json:"active" db:"active"`
Credentials string `json:"-" db:"credentials"` // Encrypted JSON
Environment string `json:"environment" db:"environment"` // sandbox, production
Commission float64 `json:"commission" db:"commission"`
UpdatedAt time.Time `json:"updated_at" db:"updated_at"`
}
// Address represents a physical location for users or companies.
type Address struct {
ID uuid.UUID `db:"id" json:"id"`
EntityID uuid.UUID `db:"entity_id" json:"entity_id"`
Title string `db:"title" json:"titulo"`
ZipCode string `db:"zip_code" json:"cep"`
Street string `db:"street" json:"logradouro"`
Number string `db:"number" json:"numero"`
Complement string `db:"complement" json:"complemento"`
District string `db:"district" json:"bairro"`
City string `db:"city" json:"cidade"`
State string `db:"state" json:"uf"`
Latitude float64 `db:"latitude" json:"latitude"`
Longitude float64 `db:"longitude" json:"longitude"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// ShippingAddress captures delivery details at order time.
type ShippingAddress struct {
RecipientName string `json:"recipient_name" db:"shipping_recipient_name"`
Street string `json:"street" db:"shipping_street"`
Number string `json:"number" db:"shipping_number"`
Complement string `json:"complement,omitempty" db:"shipping_complement"`
District string `json:"district" db:"shipping_district"`
City string `json:"city" db:"shipping_city"`
State string `json:"state" db:"shipping_state"`
ZipCode string `json:"zip_code" db:"shipping_zip_code"`
Country string `json:"country" db:"shipping_country"`
Latitude float64 `json:"latitude" db:"shipping_latitude"`
Longitude float64 `json:"longitude" db:"shipping_longitude"`
}
// ShippingSettings stores configuration for calculating delivery fees.
type ShippingSettings struct {
VendorID uuid.UUID `db:"vendor_id" json:"vendor_id"`
Active bool `db:"active" json:"active"`
MaxRadiusKm float64 `db:"max_radius_km" json:"max_radius_km"`
PricePerKmCents int64 `db:"price_per_km_cents" json:"price_per_km_cents"`
MinFeeCents int64 `db:"min_fee_cents" json:"min_fee_cents"`
FreeShippingThresholdCents *int64 `db:"free_shipping_threshold_cents" json:"free_shipping_threshold_cents"`
PickupActive bool `db:"pickup_active" json:"pickup_active"`
PickupAddress string `db:"pickup_address" json:"pickup_address"`
PickupHours string `db:"pickup_hours" json:"pickup_hours"`
Latitude float64 `db:"latitude" json:"latitude"`
Longitude float64 `db:"longitude" json:"longitude"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// Shipment stores freight label data and tracking linkage.
type Shipment struct {
ID uuid.UUID `db:"id" json:"id"`
OrderID uuid.UUID `db:"order_id" json:"order_id"`
Carrier string `db:"carrier" json:"carrier"`
TrackingCode string `db:"tracking_code" json:"tracking_code"`
ExternalTracking string `db:"external_tracking" json:"external_tracking"`
Status string `db:"status" json:"status"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// ReviewFilter captures review listing constraints.
type ReviewFilter struct {
SellerID *uuid.UUID
Limit int
Offset int
}
// ReviewPage wraps paginated review results.
type ReviewPage struct {
Reviews []Review `json:"reviews"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// ShipmentFilter captures shipment listing constraints.
type ShipmentFilter struct {
SellerID *uuid.UUID
Limit int
Offset int
}
// ShipmentPage wraps paginated shipment results.
type ShipmentPage struct {
Shipments []Shipment `json:"shipments"`
Total int64 `json:"total"`
Page int `json:"page"`
PageSize int `json:"page_size"`
}
// OrderStatus enumerates supported transitions.
type OrderStatus string
const (
OrderStatusPending OrderStatus = "Pendente"
OrderStatusPaid OrderStatus = "Pago"
OrderStatusInvoiced OrderStatus = "Faturado"
OrderStatusShipped OrderStatus = "Enviado"
OrderStatusDelivered OrderStatus = "Entregue"
OrderStatusCompleted OrderStatus = "Concluído"
OrderStatusCancelled OrderStatus = "Cancelado"
)
// PaymentMethod enumerates supported payment types.
type PaymentMethod string
const (
PaymentMethodPix PaymentMethod = "pix"
PaymentMethodCredit PaymentMethod = "credit_card"
PaymentMethodDebit PaymentMethod = "debit_card"
)
// CartItem stores buyer selections with unit pricing.
type CartItem struct {
ID uuid.UUID `db:"id" json:"id"`
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
ProductID uuid.UUID `db:"product_id" json:"product_id"`
Quantity int64 `db:"quantity" json:"quantity"`
UnitCents int64 `db:"unit_cents" json:"unit_cents"`
ProductName string `db:"product_name" json:"product_name"`
Batch string `db:"batch" json:"batch"`
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// CartSummary aggregates cart totals and discounts.
type CartSummary struct {
ID uuid.UUID `json:"id"` // Virtual Cart ID (equals BuyerID)
Items []CartItem `json:"items"`
SubtotalCents int64 `json:"subtotal_cents"`
DiscountCents int64 `json:"discount_cents"`
TotalCents int64 `json:"total_cents"`
DiscountReason string `json:"discount_reason,omitempty"`
}
// Review captures the buyer feedback for a completed order.
type Review struct {
ID uuid.UUID `db:"id" json:"id"`
OrderID uuid.UUID `db:"order_id" json:"order_id"`
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
SellerID uuid.UUID `db:"seller_id" json:"seller_id"`
Rating int `db:"rating" json:"rating"`
Comment string `db:"comment" json:"comment"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
// CompanyRating exposes the aggregate score for a seller or pharmacy.
type CompanyRating struct {
CompanyID CompanyID `json:"company_id"`
AverageScore float64 `json:"average_score"`
TotalReviews int64 `json:"total_reviews"`
}
// TopProduct aggregates seller performance per SKU.
type TopProduct struct {
ProductID uuid.UUID `db:"product_id" json:"product_id"`
Name string `db:"name" json:"name"`
TotalQuantity int64 `db:"total_quantity" json:"total_quantity"`
RevenueCents int64 `db:"revenue_cents" json:"revenue_cents"`
}
// SellerDashboard summarizes commercial metrics for sellers.
type SellerDashboard struct {
SellerID uuid.UUID `json:"seller_id"`
TotalSalesCents int64 `json:"total_sales_cents"`
OrdersCount int64 `json:"orders_count"`
TopProducts []TopProduct `json:"top_products"`
LowStockAlerts []Product `json:"low_stock_alerts"`
}
// AdminDashboard surfaces platform-wide KPIs.
type AdminDashboard struct {
GMVCents int64 `json:"gmv_cents"`
NewCompanies int64 `json:"new_companies"`
WindowStartAt time.Time `json:"window_start_at"`
}
// CompanyDocument represents a KYC/KYB document (CNPJ card, Permit).
type CompanyDocument struct {
ID uuid.UUID `db:"id" json:"id"`
CompanyID CompanyID `db:"company_id" json:"company_id"`
Type string `db:"type" json:"type"` // CNPJ, PERMIT, IDENTITY
URL string `db:"url" json:"url"`
Status string `db:"status" json:"status"` // PENDING, APPROVED, REJECTED
RejectionReason string `db:"rejection_reason" json:"rejection_reason,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// LedgerEntry represents an immutable financial record.
type LedgerEntry struct {
ID uuid.UUID `db:"id" json:"id"`
CompanyID CompanyID `db:"company_id" json:"company_id"`
AmountCents int64 `db:"amount_cents" json:"amount_cents"`
Type string `db:"type" json:"type"` // SALE, FEE, WITHDRAWAL, REFUND
Description string `db:"description" json:"description"`
ReferenceID *uuid.UUID `db:"reference_id" json:"reference_id,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}
// Withdrawal represents a payout request.
type Withdrawal struct {
ID uuid.UUID `db:"id" json:"id"`
CompanyID CompanyID `db:"company_id" json:"company_id"`
AmountCents int64 `db:"amount_cents" json:"amount_cents"`
Status string `db:"status" json:"status"` // PENDING, APPROVED, PAID, REJECTED
BankAccountInfo string `db:"bank_account_info" json:"bank_account_info"`
TransactionID string `db:"transaction_id" json:"transaction_id,omitempty"`
RejectionReason string `db:"rejection_reason" json:"rejection_reason,omitempty"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
// StockReservation holds a temporary lock on inventory during checkout.
type StockReservation struct {
ID uuid.UUID `db:"id" json:"id"`
ProductID uuid.UUID `db:"product_id" json:"product_id"`
InventoryItemID uuid.UUID `db:"inventory_item_id" json:"inventory_item_id"`
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
Quantity int64 `db:"quantity" json:"quantity"`
Status string `db:"status" json:"status"` // active, completed, expired
ExpiresAt time.Time `db:"expires_at" json:"expires_at"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
}