Compare commits

...

10 commits

8 changed files with 101 additions and 56 deletions

44
backend/.air.toml Normal file
View file

@ -0,0 +1,44 @@
root = "."
testdata_dir = "testdata"
tmp_dir = "tmp"
[build]
args_bin = []
bin = "./tmp/main"
cmd = "go build -o ./tmp/main ./cmd/api"
delay = 1000
exclude_dir = ["assets", "tmp", "vendor", "testdata"]
exclude_file = []
exclude_regex = ["_test.go"]
exclude_unchanged = false
follow_symlink = false
full_bin = ""
include_dir = ["cmd", "internal", "pkg"]
include_ext = ["go", "tpl", "tmpl", "html"]
include_regex = []
kill_delay = "0s"
log = "build-errors.log"
poll = false
poll_interval = 0
rerun = false
rerun_delay = 500
send_interrupt = false
stop_on_error = true
[color]
app = ""
build = "yellow"
main = "magenta"
runner = "green"
watcher = "cyan"
[log]
main_only = false
time = false
[misc]
clean_on_exit = false
[screen]
clear_on_rebuild = false
keep_scroll = true

View file

@ -283,6 +283,7 @@ 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"`

View file

@ -227,6 +227,7 @@ func (h *Handler) SearchProducts(w http.ResponseWriter, r *http.Request) {
expires := time.Now().AddDate(0, 0, days)
filter.ExpiresAfter = &expires
}
}
if v := r.URL.Query().Get("expires_after"); v != "" {
// Also support direct date if needed
if t, err := time.Parse("2006-01-02", v); err == nil {

View file

@ -8,7 +8,9 @@ import (
"net/http"
"strconv"
"strings"
"time"
"github.com/gofrs/uuid/v5"
"github.com/saveinmed/backend-go/internal/domain"
)

View file

@ -1,47 +0,0 @@
package tests
import (
"math"
"testing"
)
func TestPsychologicalSplitMath(t *testing.T) {
// Base Price (what the seller wants to sell for)
basePrice := 100.0
// 1. Backend inflates by 6% for search/display
buyerFeeRate := 0.06
inflatedPrice := basePrice * (1 + buyerFeeRate) // 106.0
// 2. Buyer sees 12% fee in checkout, but math must stay at 106.0
// Visual Subtotal = 106.0 / 1.12 = 94.6428...
visualSubtotal := inflatedPrice / 1.12
visualFee := inflatedPrice - visualSubtotal
// 3. Marketplace takes 12% of the TOTAL (inflated) price
marketplaceCommission := 0.12
totalMarketplaceFee := inflatedPrice * marketplaceCommission // 12.72
// 4. Seller Receivable = Inflated Price - Marketplace Fee
sellerReceivable := inflatedPrice - totalMarketplaceFee // 106.0 - 12.72 = 93.28
// 5. Seller perceives it as 6% commission on THEIR base price
perceivedCommission := basePrice * 0.06 // 6.0
expectedSellerReceivable := basePrice - perceivedCommission // 94.0
// ASSERTION: Does 93.28 == 94.0?
// Result: NO.
// To make the seller receive 94.0 (Base - 6%),
// and Marketplace receive 12.72 (which is 12% of inflated 106.0),
// The math needs adjustment.
t.Logf("Base: %.2f", basePrice)
t.Logf("Inflated (Buyer sees total): %.2f", inflatedPrice)
t.Logf("Marketplace Fee (12%% of inflated): %.2f", totalMarketplaceFee)
t.Logf("Seller Net: %.2f", sellerReceivable)
t.Logf("Target Seller Net (Base - 6%%): %.2f", expectedSellerReceivable)
if math.Abs(sellerReceivable-expectedSellerReceivable) > 0.01 {
t.Errorf("Math mismatch! Seller receives %.2f but expected %.2f", sellerReceivable, expectedSellerReceivable)
}
}

View file

@ -115,12 +115,6 @@ func (s *Service) SearchProducts(ctx context.Context, filter domain.ProductSearc
return nil, err
}
// Adjust displayed stock by subtracting reservations
// In production, the Repo query itself would handle this for better performance
// for i := range products {
// // This is a simplified adjustment.
// }
return &domain.ProductSearchPage{Products: products, Total: total, Page: page, PageSize: pageSize}, nil
}

View file

@ -93,8 +93,6 @@ type Repository interface {
GetAddress(ctx context.Context, id uuid.UUID) (*domain.Address, error)
UpdateAddress(ctx context.Context, address *domain.Address) error
DeleteAddress(ctx context.Context, id uuid.UUID) error
GeocodeAllAddresses(ctx context.Context) (int, error)
StartStockCleanupWorker(ctx context.Context)
ListManufacturers(ctx context.Context) ([]string, error)
ListCategories(ctx context.Context) ([]string, error)
@ -156,4 +154,3 @@ func NewService(
func (s *Service) GetNotificationService() notifications.NotificationService {
return s.notify
}

53
docker-compose.dev.yml Normal file
View file

@ -0,0 +1,53 @@
version: '3.8'
services:
db:
image: postgres:16-alpine
container_name: sim-db-dev
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: '123'
POSTGRES_DB: saveinmed
ports:
- '55432:5432'
volumes:
- sim-db-dev-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres -d saveinmed"]
interval: 10s
timeout: 5s
retries: 5
backend:
image: cosmtrek/air
container_name: sim-backend-dev
working_dir: /app
ports:
- "8522:8522"
environment:
- DB_URL=postgres://postgres:123@db:5432/saveinmed?sslmode=disable
- PORT=8522
volumes:
- ./backend:/app
depends_on:
db:
condition: service_healthy
frontend:
image: node:22-alpine
container_name: sim-frontend-dev
working_dir: /app
ports:
- "3001:3001"
environment:
- NEXT_PUBLIC_API_URL=https://sim.rede5.com.br/api
- NODE_ENV=development
- PORT=3001
volumes:
- ./frontend:/app
depends_on:
- backend
command: sh -c 'npm install --silent && npm run dev'
volumes:
sim-db-dev-data: