feat: Implement Payment Methods, Shipping Improvements, Swagger Audit, and UUIDv7 Migration
- Payment Methods: Added Pix/Credit/Debit selection in checkout, updated backend models and handlers. - Shipping: Updated Checkout UI, added shipping_settings table and seed data. - Swagger: Updated API docs, regenerated swagger.yaml. - UUIDv7: Migrated seeder and backend tests to use uuid.NewV7().
This commit is contained in:
parent
fd305c00a8
commit
ed4349a938
17 changed files with 1414 additions and 428 deletions
|
|
@ -9,15 +9,124 @@ const docTemplate = `{
|
||||||
"info": {
|
"info": {
|
||||||
"description": "{{escape .Description}}",
|
"description": "{{escape .Description}}",
|
||||||
"title": "{{.Title}}",
|
"title": "{{.Title}}",
|
||||||
"contact": {
|
"contact": {},
|
||||||
"name": "Engenharia SaveInMed",
|
|
||||||
"email": "devops@saveinmed.com"
|
|
||||||
},
|
|
||||||
"version": "{{.Version}}"
|
"version": "{{.Version}}"
|
||||||
},
|
},
|
||||||
"host": "{{.Host}}",
|
"host": "{{.Host}}",
|
||||||
"basePath": "{{.BasePath}}",
|
"basePath": "{{.BasePath}}",
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/api/v1/admin/reviews": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todas as avaliações (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ReviewPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/admin/shipments": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todos os envios (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ShipmentPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/auth/login": {
|
"/api/v1/auth/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Autentica usuário e retorna token JWT.",
|
"description": "Autentica usuário e retorna token JWT.",
|
||||||
|
|
@ -1506,6 +1615,60 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/reviews": {
|
"/api/v1/reviews": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todas as avaliações (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ReviewPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
|
@ -1553,6 +1716,60 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/shipments": {
|
"/api/v1/shipments": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todos os envios (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ShipmentPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
|
@ -1701,10 +1918,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"$ref": "#/definitions/domain.ShippingSettings"
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethod"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1770,10 +1984,7 @@ const docTemplate = `{
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"$ref": "#/definitions/domain.ShippingSettings"
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethod"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -2225,11 +2436,15 @@ const docTemplate = `{
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
"description": "Timestamps",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"is_24_hours": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"is_verified": {
|
"is_verified": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
|
@ -2243,6 +2458,14 @@ const docTemplate = `{
|
||||||
"longitude": {
|
"longitude": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"operating_hours": {
|
||||||
|
"description": "e.g. \"Seg-Sex: 08:00-18:00, Sab: 08:00-12:00\"",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"description": "Contact \u0026 Hours",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2312,6 +2535,9 @@ const docTemplate = `{
|
||||||
"$ref": "#/definitions/domain.OrderItem"
|
"$ref": "#/definitions/domain.OrderItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"payment_method": {
|
||||||
|
"$ref": "#/definitions/domain.PaymentMethod"
|
||||||
|
},
|
||||||
"seller_id": {
|
"seller_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2370,6 +2596,19 @@ const docTemplate = `{
|
||||||
"OrderStatusDelivered"
|
"OrderStatusDelivered"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.PaymentMethod": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"pix",
|
||||||
|
"credit_card",
|
||||||
|
"debit_card"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"PaymentMethodPix",
|
||||||
|
"PaymentMethodCredit",
|
||||||
|
"PaymentMethodDebit"
|
||||||
|
]
|
||||||
|
},
|
||||||
"domain.PaymentPreference": {
|
"domain.PaymentPreference": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2445,21 +2684,33 @@ const docTemplate = `{
|
||||||
"batch": {
|
"batch": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ean_code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"expires_at": {
|
"expires_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"observations": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"price_cents": {
|
"price_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
@ -2469,6 +2720,9 @@ const docTemplate = `{
|
||||||
"stock": {
|
"stock": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"subcategory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
@ -2520,6 +2774,9 @@ const docTemplate = `{
|
||||||
"batch": {
|
"batch": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2529,15 +2786,24 @@ const docTemplate = `{
|
||||||
"distance_km": {
|
"distance_km": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"ean_code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"expires_at": {
|
"expires_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"observations": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"price_cents": {
|
"price_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
@ -2547,6 +2813,9 @@ const docTemplate = `{
|
||||||
"stock": {
|
"stock": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"subcategory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"tenant_city": {
|
"tenant_city": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2584,6 +2853,26 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ReviewPage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"reviews": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.Review"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.SellerDashboard": {
|
"domain.SellerDashboard": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2639,6 +2928,26 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ShipmentPage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"shipments": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.Shipment"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.ShippingAddress": {
|
"domain.ShippingAddress": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2671,63 +2980,6 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.ShippingMethod": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"active": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"free_shipping_threshold_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"max_radius_km": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"min_fee_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"pickup_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"pickup_hours": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"preparation_minutes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"price_per_km_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethodType"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vendor_id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.ShippingMethodType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"pickup",
|
|
||||||
"own_delivery",
|
|
||||||
"third_party_delivery"
|
|
||||||
],
|
|
||||||
"x-enum-varnames": [
|
|
||||||
"ShippingMethodPickup",
|
|
||||||
"ShippingMethodOwnDelivery",
|
|
||||||
"ShippingMethodThirdParty"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"domain.ShippingOption": {
|
"domain.ShippingOption": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2748,6 +3000,50 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ShippingSettings": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"free_shipping_threshold_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"latitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"longitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"max_radius_km": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"min_fee_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pickup_active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pickup_address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pickup_hours": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"price_per_km_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vendor_id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.TopProduct": {
|
"domain.TopProduct": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2851,6 +3147,9 @@ const docTemplate = `{
|
||||||
"$ref": "#/definitions/domain.OrderItem"
|
"$ref": "#/definitions/domain.OrderItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"payment_method": {
|
||||||
|
"$ref": "#/definitions/domain.PaymentMethod"
|
||||||
|
},
|
||||||
"seller_id": {
|
"seller_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2938,6 +3237,9 @@ const docTemplate = `{
|
||||||
"handler.loginRequest": {
|
"handler.loginRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -3115,7 +3417,7 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handler.shippingMethodRequest": {
|
"handler.shippingSettingsRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"active": {
|
"active": {
|
||||||
|
|
@ -3124,37 +3426,30 @@ const docTemplate = `{
|
||||||
"free_shipping_threshold_cents": {
|
"free_shipping_threshold_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"latitude": {
|
||||||
|
"description": "Store location for radius calc",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"longitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"max_radius_km": {
|
"max_radius_km": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"min_fee_cents": {
|
"min_fee_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"pickup_active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"pickup_address": {
|
"pickup_address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"pickup_hours": {
|
"pickup_hours": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"preparation_minutes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"price_per_km_cents": {
|
"price_per_km_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handler.shippingSettingsRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"methods": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/handler.shippingMethodRequest"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3255,24 +3550,17 @@ const docTemplate = `{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"securityDefinitions": {
|
|
||||||
"BearerAuth": {
|
|
||||||
"type": "apiKey",
|
|
||||||
"name": "Authorization",
|
|
||||||
"in": "header"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
|
|
||||||
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
// SwaggerInfo holds exported Swagger Info so clients can modify it
|
||||||
var SwaggerInfo = &swag.Spec{
|
var SwaggerInfo = &swag.Spec{
|
||||||
Version: "1.0",
|
Version: "",
|
||||||
Host: "",
|
Host: "",
|
||||||
BasePath: "/",
|
BasePath: "",
|
||||||
Schemes: []string{"http"},
|
Schemes: []string{},
|
||||||
Title: "SaveInMed Performance Core API",
|
Title: "",
|
||||||
Description: "API REST B2B para marketplace farmacêutico com split de pagamento e rastreabilidade.",
|
Description: "",
|
||||||
InfoInstanceName: "swagger",
|
InfoInstanceName: "swagger",
|
||||||
SwaggerTemplate: docTemplate,
|
SwaggerTemplate: docTemplate,
|
||||||
LeftDelim: "{{",
|
LeftDelim: "{{",
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,121 @@
|
||||||
{
|
{
|
||||||
"schemes": [
|
|
||||||
"http"
|
|
||||||
],
|
|
||||||
"swagger": "2.0",
|
"swagger": "2.0",
|
||||||
"info": {
|
"info": {
|
||||||
"description": "API REST B2B para marketplace farmacêutico com split de pagamento e rastreabilidade.",
|
"contact": {}
|
||||||
"title": "SaveInMed Performance Core API",
|
|
||||||
"contact": {
|
|
||||||
"name": "Engenharia SaveInMed",
|
|
||||||
"email": "devops@saveinmed.com"
|
|
||||||
},
|
|
||||||
"version": "1.0"
|
|
||||||
},
|
},
|
||||||
"basePath": "/",
|
|
||||||
"paths": {
|
"paths": {
|
||||||
|
"/api/v1/admin/reviews": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todas as avaliações (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ReviewPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/admin/shipments": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todos os envios (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ShipmentPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/v1/auth/login": {
|
"/api/v1/auth/login": {
|
||||||
"post": {
|
"post": {
|
||||||
"description": "Autentica usuário e retorna token JWT.",
|
"description": "Autentica usuário e retorna token JWT.",
|
||||||
|
|
@ -1502,6 +1604,60 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/reviews": {
|
"/api/v1/reviews": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todas as avaliações (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ReviewPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
|
@ -1549,6 +1705,60 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/api/v1/shipments": {
|
"/api/v1/shipments": {
|
||||||
|
"get": {
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"BearerAuth": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"produces": [
|
||||||
|
"application/json"
|
||||||
|
],
|
||||||
|
"tags": [
|
||||||
|
"Admin"
|
||||||
|
],
|
||||||
|
"summary": "Lista todos os envios (Admin)",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Página",
|
||||||
|
"name": "page",
|
||||||
|
"in": "query"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "integer",
|
||||||
|
"description": "Tamanho da página",
|
||||||
|
"name": "page_size",
|
||||||
|
"in": "query"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "OK",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/domain.ShipmentPage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "Unauthorized",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "Internal Server Error",
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
|
|
@ -1697,10 +1907,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"$ref": "#/definitions/domain.ShippingSettings"
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethod"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -1766,10 +1973,7 @@
|
||||||
"200": {
|
"200": {
|
||||||
"description": "OK",
|
"description": "OK",
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": "array",
|
"$ref": "#/definitions/domain.ShippingSettings"
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethod"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"400": {
|
"400": {
|
||||||
|
|
@ -2221,11 +2425,15 @@
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
|
"description": "Timestamps",
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"is_24_hours": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"is_verified": {
|
"is_verified": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
|
@ -2239,6 +2447,14 @@
|
||||||
"longitude": {
|
"longitude": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"operating_hours": {
|
||||||
|
"description": "e.g. \"Seg-Sex: 08:00-18:00, Sab: 08:00-12:00\"",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"phone": {
|
||||||
|
"description": "Contact \u0026 Hours",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"state": {
|
"state": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2308,6 +2524,9 @@
|
||||||
"$ref": "#/definitions/domain.OrderItem"
|
"$ref": "#/definitions/domain.OrderItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"payment_method": {
|
||||||
|
"$ref": "#/definitions/domain.PaymentMethod"
|
||||||
|
},
|
||||||
"seller_id": {
|
"seller_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2366,6 +2585,19 @@
|
||||||
"OrderStatusDelivered"
|
"OrderStatusDelivered"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"domain.PaymentMethod": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"pix",
|
||||||
|
"credit_card",
|
||||||
|
"debit_card"
|
||||||
|
],
|
||||||
|
"x-enum-varnames": [
|
||||||
|
"PaymentMethodPix",
|
||||||
|
"PaymentMethodCredit",
|
||||||
|
"PaymentMethodDebit"
|
||||||
|
]
|
||||||
|
},
|
||||||
"domain.PaymentPreference": {
|
"domain.PaymentPreference": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2441,21 +2673,33 @@
|
||||||
"batch": {
|
"batch": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"description": {
|
"description": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"ean_code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"expires_at": {
|
"expires_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"observations": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"price_cents": {
|
"price_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
@ -2465,6 +2709,9 @@
|
||||||
"stock": {
|
"stock": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"subcategory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
}
|
}
|
||||||
|
|
@ -2516,6 +2763,9 @@
|
||||||
"batch": {
|
"batch": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2525,15 +2775,24 @@
|
||||||
"distance_km": {
|
"distance_km": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
|
"ean_code": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"expires_at": {
|
"expires_at": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"id": {
|
"id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"observations": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"price_cents": {
|
"price_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
|
@ -2543,6 +2802,9 @@
|
||||||
"stock": {
|
"stock": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"subcategory": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"tenant_city": {
|
"tenant_city": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2580,6 +2842,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ReviewPage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"reviews": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.Review"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.SellerDashboard": {
|
"domain.SellerDashboard": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2635,6 +2917,26 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ShipmentPage": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"page": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"page_size": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"shipments": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/domain.Shipment"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.ShippingAddress": {
|
"domain.ShippingAddress": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2667,63 +2969,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"domain.ShippingMethod": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"active": {
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"created_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"free_shipping_threshold_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"id": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"max_radius_km": {
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"min_fee_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"pickup_address": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"pickup_hours": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"preparation_minutes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"price_per_km_cents": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"$ref": "#/definitions/domain.ShippingMethodType"
|
|
||||||
},
|
|
||||||
"updated_at": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"vendor_id": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"domain.ShippingMethodType": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"pickup",
|
|
||||||
"own_delivery",
|
|
||||||
"third_party_delivery"
|
|
||||||
],
|
|
||||||
"x-enum-varnames": [
|
|
||||||
"ShippingMethodPickup",
|
|
||||||
"ShippingMethodOwnDelivery",
|
|
||||||
"ShippingMethodThirdParty"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"domain.ShippingOption": {
|
"domain.ShippingOption": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2744,6 +2989,50 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"domain.ShippingSettings": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"created_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"free_shipping_threshold_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"latitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"longitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"max_radius_km": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"min_fee_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"pickup_active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"pickup_address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pickup_hours": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"price_per_km_cents": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"updated_at": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vendor_id": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"domain.TopProduct": {
|
"domain.TopProduct": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|
@ -2847,6 +3136,9 @@
|
||||||
"$ref": "#/definitions/domain.OrderItem"
|
"$ref": "#/definitions/domain.OrderItem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"payment_method": {
|
||||||
|
"$ref": "#/definitions/domain.PaymentMethod"
|
||||||
|
},
|
||||||
"seller_id": {
|
"seller_id": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -2934,6 +3226,9 @@
|
||||||
"handler.loginRequest": {
|
"handler.loginRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
|
"email": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"password": {
|
"password": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
|
@ -3111,7 +3406,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"handler.shippingMethodRequest": {
|
"handler.shippingSettingsRequest": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"active": {
|
"active": {
|
||||||
|
|
@ -3120,37 +3415,30 @@
|
||||||
"free_shipping_threshold_cents": {
|
"free_shipping_threshold_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"latitude": {
|
||||||
|
"description": "Store location for radius calc",
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"longitude": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
"max_radius_km": {
|
"max_radius_km": {
|
||||||
"type": "number"
|
"type": "number"
|
||||||
},
|
},
|
||||||
"min_fee_cents": {
|
"min_fee_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
},
|
||||||
|
"pickup_active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"pickup_address": {
|
"pickup_address": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"pickup_hours": {
|
"pickup_hours": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
"preparation_minutes": {
|
|
||||||
"type": "integer"
|
|
||||||
},
|
|
||||||
"price_per_km_cents": {
|
"price_per_km_cents": {
|
||||||
"type": "integer"
|
"type": "integer"
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"handler.shippingSettingsRequest": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"methods": {
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"$ref": "#/definitions/handler.shippingMethodRequest"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -3251,12 +3539,5 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"securityDefinitions": {
|
|
||||||
"BearerAuth": {
|
|
||||||
"type": "apiKey",
|
|
||||||
"name": "Authorization",
|
|
||||||
"in": "header"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
basePath: /
|
|
||||||
definitions:
|
definitions:
|
||||||
domain.AdminDashboard:
|
domain.AdminDashboard:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -59,9 +58,12 @@ definitions:
|
||||||
corporate_name:
|
corporate_name:
|
||||||
type: string
|
type: string
|
||||||
created_at:
|
created_at:
|
||||||
|
description: Timestamps
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
is_24_hours:
|
||||||
|
type: boolean
|
||||||
is_verified:
|
is_verified:
|
||||||
type: boolean
|
type: boolean
|
||||||
latitude:
|
latitude:
|
||||||
|
|
@ -71,6 +73,12 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
longitude:
|
longitude:
|
||||||
type: number
|
type: number
|
||||||
|
operating_hours:
|
||||||
|
description: 'e.g. "Seg-Sex: 08:00-18:00, Sab: 08:00-12:00"'
|
||||||
|
type: string
|
||||||
|
phone:
|
||||||
|
description: Contact & Hours
|
||||||
|
type: string
|
||||||
state:
|
state:
|
||||||
type: string
|
type: string
|
||||||
updated_at:
|
updated_at:
|
||||||
|
|
@ -116,6 +124,8 @@ definitions:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/domain.OrderItem'
|
$ref: '#/definitions/domain.OrderItem'
|
||||||
type: array
|
type: array
|
||||||
|
payment_method:
|
||||||
|
$ref: '#/definitions/domain.PaymentMethod'
|
||||||
seller_id:
|
seller_id:
|
||||||
type: string
|
type: string
|
||||||
shipping:
|
shipping:
|
||||||
|
|
@ -156,6 +166,16 @@ definitions:
|
||||||
- OrderStatusPaid
|
- OrderStatusPaid
|
||||||
- OrderStatusInvoiced
|
- OrderStatusInvoiced
|
||||||
- OrderStatusDelivered
|
- OrderStatusDelivered
|
||||||
|
domain.PaymentMethod:
|
||||||
|
enum:
|
||||||
|
- pix
|
||||||
|
- credit_card
|
||||||
|
- debit_card
|
||||||
|
type: string
|
||||||
|
x-enum-varnames:
|
||||||
|
- PaymentMethodPix
|
||||||
|
- PaymentMethodCredit
|
||||||
|
- PaymentMethodDebit
|
||||||
domain.PaymentPreference:
|
domain.PaymentPreference:
|
||||||
properties:
|
properties:
|
||||||
commission_pct:
|
commission_pct:
|
||||||
|
|
@ -205,22 +225,32 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
batch:
|
batch:
|
||||||
type: string
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
|
ean_code:
|
||||||
|
type: string
|
||||||
expires_at:
|
expires_at:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
manufacturer:
|
||||||
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
observations:
|
||||||
|
type: string
|
||||||
price_cents:
|
price_cents:
|
||||||
type: integer
|
type: integer
|
||||||
seller_id:
|
seller_id:
|
||||||
type: string
|
type: string
|
||||||
stock:
|
stock:
|
||||||
type: integer
|
type: integer
|
||||||
|
subcategory:
|
||||||
|
type: string
|
||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
|
@ -254,24 +284,34 @@ definitions:
|
||||||
properties:
|
properties:
|
||||||
batch:
|
batch:
|
||||||
type: string
|
type: string
|
||||||
|
category:
|
||||||
|
type: string
|
||||||
created_at:
|
created_at:
|
||||||
type: string
|
type: string
|
||||||
description:
|
description:
|
||||||
type: string
|
type: string
|
||||||
distance_km:
|
distance_km:
|
||||||
type: number
|
type: number
|
||||||
|
ean_code:
|
||||||
|
type: string
|
||||||
expires_at:
|
expires_at:
|
||||||
type: string
|
type: string
|
||||||
id:
|
id:
|
||||||
type: string
|
type: string
|
||||||
|
manufacturer:
|
||||||
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
observations:
|
||||||
|
type: string
|
||||||
price_cents:
|
price_cents:
|
||||||
type: integer
|
type: integer
|
||||||
seller_id:
|
seller_id:
|
||||||
type: string
|
type: string
|
||||||
stock:
|
stock:
|
||||||
type: integer
|
type: integer
|
||||||
|
subcategory:
|
||||||
|
type: string
|
||||||
tenant_city:
|
tenant_city:
|
||||||
type: string
|
type: string
|
||||||
tenant_state:
|
tenant_state:
|
||||||
|
|
@ -296,6 +336,19 @@ definitions:
|
||||||
seller_id:
|
seller_id:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
domain.ReviewPage:
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
page_size:
|
||||||
|
type: integer
|
||||||
|
reviews:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.Review'
|
||||||
|
type: array
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
domain.SellerDashboard:
|
domain.SellerDashboard:
|
||||||
properties:
|
properties:
|
||||||
low_stock_alerts:
|
low_stock_alerts:
|
||||||
|
|
@ -332,6 +385,19 @@ definitions:
|
||||||
updated_at:
|
updated_at:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
|
domain.ShipmentPage:
|
||||||
|
properties:
|
||||||
|
page:
|
||||||
|
type: integer
|
||||||
|
page_size:
|
||||||
|
type: integer
|
||||||
|
shipments:
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/domain.Shipment'
|
||||||
|
type: array
|
||||||
|
total:
|
||||||
|
type: integer
|
||||||
|
type: object
|
||||||
domain.ShippingAddress:
|
domain.ShippingAddress:
|
||||||
properties:
|
properties:
|
||||||
city:
|
city:
|
||||||
|
|
@ -353,45 +419,6 @@ definitions:
|
||||||
zip_code:
|
zip_code:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
domain.ShippingMethod:
|
|
||||||
properties:
|
|
||||||
active:
|
|
||||||
type: boolean
|
|
||||||
created_at:
|
|
||||||
type: string
|
|
||||||
free_shipping_threshold_cents:
|
|
||||||
type: integer
|
|
||||||
id:
|
|
||||||
type: string
|
|
||||||
max_radius_km:
|
|
||||||
type: number
|
|
||||||
min_fee_cents:
|
|
||||||
type: integer
|
|
||||||
pickup_address:
|
|
||||||
type: string
|
|
||||||
pickup_hours:
|
|
||||||
type: string
|
|
||||||
preparation_minutes:
|
|
||||||
type: integer
|
|
||||||
price_per_km_cents:
|
|
||||||
type: integer
|
|
||||||
type:
|
|
||||||
$ref: '#/definitions/domain.ShippingMethodType'
|
|
||||||
updated_at:
|
|
||||||
type: string
|
|
||||||
vendor_id:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
domain.ShippingMethodType:
|
|
||||||
enum:
|
|
||||||
- pickup
|
|
||||||
- own_delivery
|
|
||||||
- third_party_delivery
|
|
||||||
type: string
|
|
||||||
x-enum-varnames:
|
|
||||||
- ShippingMethodPickup
|
|
||||||
- ShippingMethodOwnDelivery
|
|
||||||
- ShippingMethodThirdParty
|
|
||||||
domain.ShippingOption:
|
domain.ShippingOption:
|
||||||
properties:
|
properties:
|
||||||
description:
|
description:
|
||||||
|
|
@ -405,6 +432,35 @@ definitions:
|
||||||
value_cents:
|
value_cents:
|
||||||
type: integer
|
type: integer
|
||||||
type: object
|
type: object
|
||||||
|
domain.ShippingSettings:
|
||||||
|
properties:
|
||||||
|
active:
|
||||||
|
type: boolean
|
||||||
|
created_at:
|
||||||
|
type: string
|
||||||
|
free_shipping_threshold_cents:
|
||||||
|
type: integer
|
||||||
|
latitude:
|
||||||
|
type: number
|
||||||
|
longitude:
|
||||||
|
type: number
|
||||||
|
max_radius_km:
|
||||||
|
type: number
|
||||||
|
min_fee_cents:
|
||||||
|
type: integer
|
||||||
|
pickup_active:
|
||||||
|
type: boolean
|
||||||
|
pickup_address:
|
||||||
|
type: string
|
||||||
|
pickup_hours:
|
||||||
|
type: string
|
||||||
|
price_per_km_cents:
|
||||||
|
type: integer
|
||||||
|
updated_at:
|
||||||
|
type: string
|
||||||
|
vendor_id:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
domain.TopProduct:
|
domain.TopProduct:
|
||||||
properties:
|
properties:
|
||||||
name:
|
name:
|
||||||
|
|
@ -472,6 +528,8 @@ definitions:
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/domain.OrderItem'
|
$ref: '#/definitions/domain.OrderItem'
|
||||||
type: array
|
type: array
|
||||||
|
payment_method:
|
||||||
|
$ref: '#/definitions/domain.PaymentMethod'
|
||||||
seller_id:
|
seller_id:
|
||||||
type: string
|
type: string
|
||||||
shipping:
|
shipping:
|
||||||
|
|
@ -528,6 +586,8 @@ definitions:
|
||||||
type: object
|
type: object
|
||||||
handler.loginRequest:
|
handler.loginRequest:
|
||||||
properties:
|
properties:
|
||||||
|
email:
|
||||||
|
type: string
|
||||||
password:
|
password:
|
||||||
type: string
|
type: string
|
||||||
username:
|
username:
|
||||||
|
|
@ -643,33 +703,29 @@ definitions:
|
||||||
vendor_id:
|
vendor_id:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
handler.shippingMethodRequest:
|
handler.shippingSettingsRequest:
|
||||||
properties:
|
properties:
|
||||||
active:
|
active:
|
||||||
type: boolean
|
type: boolean
|
||||||
free_shipping_threshold_cents:
|
free_shipping_threshold_cents:
|
||||||
type: integer
|
type: integer
|
||||||
|
latitude:
|
||||||
|
description: Store location for radius calc
|
||||||
|
type: number
|
||||||
|
longitude:
|
||||||
|
type: number
|
||||||
max_radius_km:
|
max_radius_km:
|
||||||
type: number
|
type: number
|
||||||
min_fee_cents:
|
min_fee_cents:
|
||||||
type: integer
|
type: integer
|
||||||
|
pickup_active:
|
||||||
|
type: boolean
|
||||||
pickup_address:
|
pickup_address:
|
||||||
type: string
|
type: string
|
||||||
pickup_hours:
|
pickup_hours:
|
||||||
type: string
|
type: string
|
||||||
preparation_minutes:
|
|
||||||
type: integer
|
|
||||||
price_per_km_cents:
|
price_per_km_cents:
|
||||||
type: integer
|
type: integer
|
||||||
type:
|
|
||||||
type: string
|
|
||||||
type: object
|
|
||||||
handler.shippingSettingsRequest:
|
|
||||||
properties:
|
|
||||||
methods:
|
|
||||||
items:
|
|
||||||
$ref: '#/definitions/handler.shippingMethodRequest'
|
|
||||||
type: array
|
|
||||||
type: object
|
type: object
|
||||||
handler.updateCompanyRequest:
|
handler.updateCompanyRequest:
|
||||||
properties:
|
properties:
|
||||||
|
|
@ -735,14 +791,78 @@ definitions:
|
||||||
type: string
|
type: string
|
||||||
type: object
|
type: object
|
||||||
info:
|
info:
|
||||||
contact:
|
contact: {}
|
||||||
email: devops@saveinmed.com
|
|
||||||
name: Engenharia SaveInMed
|
|
||||||
description: API REST B2B para marketplace farmacêutico com split de pagamento e
|
|
||||||
rastreabilidade.
|
|
||||||
title: SaveInMed Performance Core API
|
|
||||||
version: "1.0"
|
|
||||||
paths:
|
paths:
|
||||||
|
/api/v1/admin/reviews:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: Página
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: Tamanho da página
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ReviewPage'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: Lista todas as avaliações (Admin)
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
|
/api/v1/admin/shipments:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: Página
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: Tamanho da página
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ShipmentPage'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: Lista todos os envios (Admin)
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
/api/v1/auth/login:
|
/api/v1/auth/login:
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
|
|
@ -1697,6 +1817,40 @@ paths:
|
||||||
tags:
|
tags:
|
||||||
- Produtos
|
- Produtos
|
||||||
/api/v1/reviews:
|
/api/v1/reviews:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: Página
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: Tamanho da página
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ReviewPage'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: Lista todas as avaliações (Admin)
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
|
|
@ -1726,6 +1880,40 @@ paths:
|
||||||
tags:
|
tags:
|
||||||
- Avaliações
|
- Avaliações
|
||||||
/api/v1/shipments:
|
/api/v1/shipments:
|
||||||
|
get:
|
||||||
|
parameters:
|
||||||
|
- description: Página
|
||||||
|
in: query
|
||||||
|
name: page
|
||||||
|
type: integer
|
||||||
|
- description: Tamanho da página
|
||||||
|
in: query
|
||||||
|
name: page_size
|
||||||
|
type: integer
|
||||||
|
produces:
|
||||||
|
- application/json
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: OK
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/domain.ShipmentPage'
|
||||||
|
"401":
|
||||||
|
description: Unauthorized
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
"500":
|
||||||
|
description: Internal Server Error
|
||||||
|
schema:
|
||||||
|
additionalProperties:
|
||||||
|
type: string
|
||||||
|
type: object
|
||||||
|
security:
|
||||||
|
- BearerAuth: []
|
||||||
|
summary: Lista todos os envios (Admin)
|
||||||
|
tags:
|
||||||
|
- Admin
|
||||||
post:
|
post:
|
||||||
consumes:
|
consumes:
|
||||||
- application/json
|
- application/json
|
||||||
|
|
@ -1820,9 +2008,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
$ref: '#/definitions/domain.ShippingSettings'
|
||||||
$ref: '#/definitions/domain.ShippingMethod'
|
|
||||||
type: array
|
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -1866,9 +2052,7 @@ paths:
|
||||||
"200":
|
"200":
|
||||||
description: OK
|
description: OK
|
||||||
schema:
|
schema:
|
||||||
items:
|
$ref: '#/definitions/domain.ShippingSettings'
|
||||||
$ref: '#/definitions/domain.ShippingMethod'
|
|
||||||
type: array
|
|
||||||
"400":
|
"400":
|
||||||
description: Bad Request
|
description: Bad Request
|
||||||
schema:
|
schema:
|
||||||
|
|
@ -2099,11 +2283,4 @@ paths:
|
||||||
summary: Atualizar usuário
|
summary: Atualizar usuário
|
||||||
tags:
|
tags:
|
||||||
- Usuários
|
- Usuários
|
||||||
schemes:
|
|
||||||
- http
|
|
||||||
securityDefinitions:
|
|
||||||
BearerAuth:
|
|
||||||
in: header
|
|
||||||
name: Authorization
|
|
||||||
type: apiKey
|
|
||||||
swagger: "2.0"
|
swagger: "2.0"
|
||||||
|
|
|
||||||
|
|
@ -184,15 +184,16 @@ type InventoryAdjustment struct {
|
||||||
|
|
||||||
// Order captures the status lifecycle and payment intent.
|
// Order captures the status lifecycle and payment intent.
|
||||||
type Order struct {
|
type Order struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
|
BuyerID uuid.UUID `db:"buyer_id" json:"buyer_id"`
|
||||||
SellerID uuid.UUID `db:"seller_id" json:"seller_id"`
|
SellerID uuid.UUID `db:"seller_id" json:"seller_id"`
|
||||||
Status OrderStatus `db:"status" json:"status"`
|
Status OrderStatus `db:"status" json:"status"`
|
||||||
TotalCents int64 `db:"total_cents" json:"total_cents"`
|
TotalCents int64 `db:"total_cents" json:"total_cents"`
|
||||||
Items []OrderItem `json:"items"`
|
PaymentMethod PaymentMethod `db:"payment_method" json:"payment_method"`
|
||||||
Shipping ShippingAddress `json:"shipping"`
|
Items []OrderItem `json:"items"`
|
||||||
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
Shipping ShippingAddress `json:"shipping"`
|
||||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
CreatedAt time.Time `db:"created_at" json:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// OrderItem stores SKU-level batch tracking.
|
// OrderItem stores SKU-level batch tracking.
|
||||||
|
|
@ -316,6 +317,15 @@ const (
|
||||||
OrderStatusDelivered OrderStatus = "Entregue"
|
OrderStatusDelivered OrderStatus = "Entregue"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 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.
|
// CartItem stores buyer selections with unit pricing.
|
||||||
type CartItem struct {
|
type CartItem struct {
|
||||||
ID uuid.UUID `db:"id" json:"id"`
|
ID uuid.UUID `db:"id" json:"id"`
|
||||||
|
|
|
||||||
|
|
@ -155,10 +155,11 @@ type updateProductRequest struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type createOrderRequest struct {
|
type createOrderRequest struct {
|
||||||
BuyerID uuid.UUID `json:"buyer_id"`
|
BuyerID uuid.UUID `json:"buyer_id"`
|
||||||
SellerID uuid.UUID `json:"seller_id"`
|
SellerID uuid.UUID `json:"seller_id"`
|
||||||
Items []domain.OrderItem `json:"items"`
|
Items []domain.OrderItem `json:"items"`
|
||||||
Shipping domain.ShippingAddress `json:"shipping"`
|
Shipping domain.ShippingAddress `json:"shipping"`
|
||||||
|
PaymentMethod domain.PaymentMethod `json:"payment_method"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type createShipmentRequest struct {
|
type createShipmentRequest struct {
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ func NewMockRepository() *MockRepository {
|
||||||
|
|
||||||
// Company methods
|
// Company methods
|
||||||
func (m *MockRepository) CreateCompany(ctx context.Context, company *domain.Company) error {
|
func (m *MockRepository) CreateCompany(ctx context.Context, company *domain.Company) error {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
company.ID = id
|
company.ID = id
|
||||||
company.CreatedAt = time.Now()
|
company.CreatedAt = time.Now()
|
||||||
company.UpdatedAt = time.Now()
|
company.UpdatedAt = time.Now()
|
||||||
|
|
@ -81,7 +81,7 @@ func (m *MockRepository) DeleteCompany(ctx context.Context, id uuid.UUID) error
|
||||||
|
|
||||||
// Product methods
|
// Product methods
|
||||||
func (m *MockRepository) CreateProduct(ctx context.Context, product *domain.Product) error {
|
func (m *MockRepository) CreateProduct(ctx context.Context, product *domain.Product) error {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
product.ID = id
|
product.ID = id
|
||||||
product.CreatedAt = time.Now()
|
product.CreatedAt = time.Now()
|
||||||
product.UpdatedAt = time.Now()
|
product.UpdatedAt = time.Now()
|
||||||
|
|
@ -140,7 +140,7 @@ func (m *MockRepository) SearchProducts(ctx context.Context, filter domain.Produ
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRepository) CreateOrder(ctx context.Context, order *domain.Order) error {
|
func (m *MockRepository) CreateOrder(ctx context.Context, order *domain.Order) error {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
order.ID = id
|
order.ID = id
|
||||||
m.orders = append(m.orders, *order)
|
m.orders = append(m.orders, *order)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -176,7 +176,7 @@ func (m *MockRepository) GetShipmentByOrderID(ctx context.Context, orderID uuid.
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MockRepository) CreateUser(ctx context.Context, user *domain.User) error {
|
func (m *MockRepository) CreateUser(ctx context.Context, user *domain.User) error {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
user.ID = id
|
user.ID = id
|
||||||
m.users = append(m.users, *user)
|
m.users = append(m.users, *user)
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -365,7 +365,7 @@ func TestCreateCompany(t *testing.T) {
|
||||||
func TestCreateProduct(t *testing.T) {
|
func TestCreateProduct(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
|
|
||||||
sellerID, _ := uuid.NewV4()
|
sellerID, _ := uuid.NewV7()
|
||||||
payload := `{"seller_id":"` + sellerID.String() + `","name":"Aspirin","description":"Pain relief","batch":"BATCH-001","expires_at":"2025-12-31T00:00:00Z","price_cents":1000,"stock":100}`
|
payload := `{"seller_id":"` + sellerID.String() + `","name":"Aspirin","description":"Pain relief","batch":"BATCH-001","expires_at":"2025-12-31T00:00:00Z","price_cents":1000,"stock":100}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/products", bytes.NewReader([]byte(payload)))
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/products", bytes.NewReader([]byte(payload)))
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
@ -401,7 +401,7 @@ func TestAdminLogin_Success(t *testing.T) {
|
||||||
h := New(svc)
|
h := New(svc)
|
||||||
|
|
||||||
// Create admin user through service (which hashes password)
|
// Create admin user through service (which hashes password)
|
||||||
companyID, _ := uuid.NewV4()
|
companyID, _ := uuid.NewV7()
|
||||||
user := &domain.User{
|
user := &domain.User{
|
||||||
CompanyID: companyID,
|
CompanyID: companyID,
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
|
|
@ -445,7 +445,7 @@ func TestAdminLogin_WrongPassword(t *testing.T) {
|
||||||
h := New(svc)
|
h := New(svc)
|
||||||
|
|
||||||
// Create admin user
|
// Create admin user
|
||||||
companyID, _ := uuid.NewV4()
|
companyID, _ := uuid.NewV7()
|
||||||
user := &domain.User{
|
user := &domain.User{
|
||||||
CompanyID: companyID,
|
CompanyID: companyID,
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
|
|
@ -519,7 +519,7 @@ func TestRegister_MissingCompany(t *testing.T) {
|
||||||
|
|
||||||
func TestGetCompany_NotFound(t *testing.T) {
|
func TestGetCompany_NotFound(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/companies/"+id.String(), nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/companies/"+id.String(), nil)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.GetCompany(rec, req)
|
h.GetCompany(rec, req)
|
||||||
|
|
@ -540,7 +540,7 @@ func TestGetCompany_InvalidUUID(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateCompany_InvalidJSON(t *testing.T) {
|
func TestUpdateCompany_InvalidJSON(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/companies/"+id.String(), bytes.NewReader([]byte("{")))
|
req := httptest.NewRequest(http.MethodPatch, "/api/v1/companies/"+id.String(), bytes.NewReader([]byte("{")))
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.UpdateCompany(rec, req)
|
h.UpdateCompany(rec, req)
|
||||||
|
|
@ -573,7 +573,7 @@ func TestVerifyCompany_InvalidPath(t *testing.T) {
|
||||||
|
|
||||||
func TestGetProduct_NotFound(t *testing.T) {
|
func TestGetProduct_NotFound(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/products/"+id.String(), nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/products/"+id.String(), nil)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.GetProduct(rec, req)
|
h.GetProduct(rec, req)
|
||||||
|
|
@ -584,7 +584,7 @@ func TestGetProduct_NotFound(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateProduct_InvalidJSON(t *testing.T) {
|
func TestUpdateProduct_InvalidJSON(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/products/"+id.String(), bytes.NewReader([]byte("{")))
|
req := httptest.NewRequest(http.MethodPatch, "/api/v1/products/"+id.String(), bytes.NewReader([]byte("{")))
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.UpdateProduct(rec, req)
|
h.UpdateProduct(rec, req)
|
||||||
|
|
@ -637,7 +637,7 @@ func TestAdjustInventory_InvalidJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestAdjustInventory_ZeroDelta(t *testing.T) {
|
func TestAdjustInventory_ZeroDelta(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
payload := `{"product_id":"` + id.String() + `","delta":0,"reason":"test"}`
|
payload := `{"product_id":"` + id.String() + `","delta":0,"reason":"test"}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/inventory/adjust", bytes.NewReader([]byte(payload)))
|
req := httptest.NewRequest(http.MethodPost, "/api/v1/inventory/adjust", bytes.NewReader([]byte(payload)))
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|
@ -661,7 +661,7 @@ func TestCreateOrder_InvalidJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestGetOrder_NotFound(t *testing.T) {
|
func TestGetOrder_NotFound(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/orders/"+id.String(), nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/orders/"+id.String(), nil)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.GetOrder(rec, req)
|
h.GetOrder(rec, req)
|
||||||
|
|
@ -672,7 +672,7 @@ func TestGetOrder_NotFound(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateOrderStatus_InvalidStatus(t *testing.T) {
|
func TestUpdateOrderStatus_InvalidStatus(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
payload := `{"status":"invalid_status"}`
|
payload := `{"status":"invalid_status"}`
|
||||||
req := httptest.NewRequest(http.MethodPatch, "/api/v1/orders/"+id.String()+"/status", bytes.NewReader([]byte(payload)))
|
req := httptest.NewRequest(http.MethodPatch, "/api/v1/orders/"+id.String()+"/status", bytes.NewReader([]byte(payload)))
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|
@ -783,7 +783,7 @@ func TestListUsers_Success(t *testing.T) {
|
||||||
|
|
||||||
func TestGetUser_NotFound(t *testing.T) {
|
func TestGetUser_NotFound(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/users/"+id.String(), nil)
|
req := httptest.NewRequest(http.MethodGet, "/api/v1/users/"+id.String(), nil)
|
||||||
req.Header.Set("X-User-Role", "Admin")
|
req.Header.Set("X-User-Role", "Admin")
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|
@ -806,7 +806,7 @@ func TestCreateUser_InvalidJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestUpdateUser_InvalidJSON(t *testing.T) {
|
func TestUpdateUser_InvalidJSON(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodPut, "/api/v1/users/"+id.String(), bytes.NewReader([]byte("{")))
|
req := httptest.NewRequest(http.MethodPut, "/api/v1/users/"+id.String(), bytes.NewReader([]byte("{")))
|
||||||
req.Header.Set("X-User-Role", "Admin")
|
req.Header.Set("X-User-Role", "Admin")
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|
@ -851,7 +851,7 @@ func TestAddToCart_InvalidJSON(t *testing.T) {
|
||||||
|
|
||||||
func TestDeleteCartItem_NoContext(t *testing.T) {
|
func TestDeleteCartItem_NoContext(t *testing.T) {
|
||||||
h := newTestHandler()
|
h := newTestHandler()
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
req := httptest.NewRequest(http.MethodDelete, "/api/v1/cart/"+id.String(), nil)
|
req := httptest.NewRequest(http.MethodDelete, "/api/v1/cart/"+id.String(), nil)
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
h.DeleteCartItem(rec, req)
|
h.DeleteCartItem(rec, req)
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,11 @@ func (h *Handler) CreateOrder(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
BuyerID: req.BuyerID,
|
BuyerID: req.BuyerID,
|
||||||
SellerID: req.SellerID,
|
SellerID: req.SellerID,
|
||||||
Items: req.Items,
|
Items: req.Items,
|
||||||
Shipping: req.Shipping,
|
Shipping: req.Shipping,
|
||||||
|
PaymentMethod: req.PaymentMethod,
|
||||||
}
|
}
|
||||||
|
|
||||||
var total int64
|
var total int64
|
||||||
|
|
|
||||||
|
|
@ -113,8 +113,8 @@ func createTestToken(secret string, userID uuid.UUID, role string, companyID *uu
|
||||||
|
|
||||||
func TestRequireAuthValidToken(t *testing.T) {
|
func TestRequireAuthValidToken(t *testing.T) {
|
||||||
secret := "test-secret"
|
secret := "test-secret"
|
||||||
userID, _ := uuid.NewV4()
|
userID, _ := uuid.NewV7()
|
||||||
companyID, _ := uuid.NewV4()
|
companyID, _ := uuid.NewV7()
|
||||||
tokenStr := createTestToken(secret, userID, "Admin", &companyID)
|
tokenStr := createTestToken(secret, userID, "Admin", &companyID)
|
||||||
|
|
||||||
var receivedClaims Claims
|
var receivedClaims Claims
|
||||||
|
|
@ -172,7 +172,7 @@ func TestRequireAuthInvalidToken(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRequireAuthWrongSecret(t *testing.T) {
|
func TestRequireAuthWrongSecret(t *testing.T) {
|
||||||
userID, _ := uuid.NewV4()
|
userID, _ := uuid.NewV7()
|
||||||
tokenStr := createTestToken("correct-secret", userID, "User", nil)
|
tokenStr := createTestToken("correct-secret", userID, "User", nil)
|
||||||
|
|
||||||
handler := RequireAuth([]byte("wrong-secret"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := RequireAuth([]byte("wrong-secret"))(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -192,7 +192,7 @@ func TestRequireAuthWrongSecret(t *testing.T) {
|
||||||
|
|
||||||
func TestRequireAuthRoleRestriction(t *testing.T) {
|
func TestRequireAuthRoleRestriction(t *testing.T) {
|
||||||
secret := "secret"
|
secret := "secret"
|
||||||
userID, _ := uuid.NewV4()
|
userID, _ := uuid.NewV7()
|
||||||
tokenStr := createTestToken(secret, userID, "User", nil)
|
tokenStr := createTestToken(secret, userID, "User", nil)
|
||||||
|
|
||||||
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -212,7 +212,7 @@ func TestRequireAuthRoleRestriction(t *testing.T) {
|
||||||
|
|
||||||
func TestRequireAuthRoleAllowed(t *testing.T) {
|
func TestRequireAuthRoleAllowed(t *testing.T) {
|
||||||
secret := "secret"
|
secret := "secret"
|
||||||
userID, _ := uuid.NewV4()
|
userID, _ := uuid.NewV7()
|
||||||
tokenStr := createTestToken(secret, userID, "Admin", nil)
|
tokenStr := createTestToken(secret, userID, "Admin", nil)
|
||||||
|
|
||||||
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
handler := RequireAuth([]byte(secret), "Admin")(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
@ -232,7 +232,7 @@ func TestRequireAuthRoleAllowed(t *testing.T) {
|
||||||
|
|
||||||
func TestGetClaimsFromContext(t *testing.T) {
|
func TestGetClaimsFromContext(t *testing.T) {
|
||||||
claims := Claims{
|
claims := Claims{
|
||||||
UserID: uuid.Must(uuid.NewV4()),
|
UserID: uuid.Must(uuid.NewV7()),
|
||||||
Role: "Admin",
|
Role: "Admin",
|
||||||
}
|
}
|
||||||
ctx := context.WithValue(context.Background(), claimsKey, claims)
|
ctx := context.WithValue(context.Background(), claimsKey, claims)
|
||||||
|
|
@ -345,7 +345,7 @@ func TestCORSLegacyWrapper(t *testing.T) {
|
||||||
|
|
||||||
func TestRequireAuthExpiredToken(t *testing.T) {
|
func TestRequireAuthExpiredToken(t *testing.T) {
|
||||||
secret := "test-secret"
|
secret := "test-secret"
|
||||||
userID, _ := uuid.NewV4()
|
userID, _ := uuid.NewV7()
|
||||||
|
|
||||||
// Create an expired token
|
// Create an expired token
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
|
||||||
|
|
|
||||||
|
|
@ -24,9 +24,9 @@ func TestCreatePreference(t *testing.T) {
|
||||||
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: uuid.Must(uuid.NewV4()),
|
BuyerID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 10000, // R$100
|
TotalCents: 10000, // R$100
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -83,7 +83,7 @@ func TestCreatePreferenceWithDifferentCommissions(t *testing.T) {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
gateway := NewMercadoPagoGateway("https://test.com", tc.commission)
|
gateway := NewMercadoPagoGateway("https://test.com", tc.commission)
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: tc.totalCents,
|
TotalCents: tc.totalCents,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,7 +106,7 @@ func TestCreatePreferenceWithCancelledContext(t *testing.T) {
|
||||||
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ func TestCreatePreferenceWithTimeout(t *testing.T) {
|
||||||
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,7 +144,7 @@ func TestCreatePreferenceWithZeroTotal(t *testing.T) {
|
||||||
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
gateway := NewMercadoPagoGateway("https://api.mercadopago.com", 2.5)
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 0,
|
TotalCents: 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -459,9 +459,9 @@ func (r *Repository) CreateOrder(ctx context.Context, order *domain.Order) error
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
orderQuery := `INSERT INTO orders (id, buyer_id, seller_id, status, total_cents, shipping_recipient_name, shipping_street, shipping_number, shipping_complement, shipping_district, shipping_city, shipping_state, shipping_zip_code, shipping_country, created_at, updated_at)
|
orderQuery := `INSERT INTO orders (id, buyer_id, seller_id, status, total_cents, payment_method, shipping_recipient_name, shipping_street, shipping_number, shipping_complement, shipping_district, shipping_city, shipping_state, shipping_zip_code, shipping_country, created_at, updated_at)
|
||||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)`
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17)`
|
||||||
if _, err := tx.ExecContext(ctx, orderQuery, order.ID, order.BuyerID, order.SellerID, order.Status, order.TotalCents, order.Shipping.RecipientName, order.Shipping.Street, order.Shipping.Number, order.Shipping.Complement, order.Shipping.District, order.Shipping.City, order.Shipping.State, order.Shipping.ZipCode, order.Shipping.Country, order.CreatedAt, order.UpdatedAt); err != nil {
|
if _, err := tx.ExecContext(ctx, orderQuery, order.ID, order.BuyerID, order.SellerID, order.Status, order.TotalCents, order.PaymentMethod, order.Shipping.RecipientName, order.Shipping.Street, order.Shipping.Number, order.Shipping.Complement, order.Shipping.District, order.Shipping.City, order.Shipping.State, order.Shipping.ZipCode, order.Shipping.Country, order.CreatedAt, order.UpdatedAt); err != nil {
|
||||||
_ = tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -528,25 +528,26 @@ func (r *Repository) ListOrders(ctx context.Context, filter domain.OrderFilter)
|
||||||
filter.Limit = 20
|
filter.Limit = 20
|
||||||
}
|
}
|
||||||
args = append(args, filter.Limit, filter.Offset)
|
args = append(args, filter.Limit, filter.Offset)
|
||||||
listQuery := fmt.Sprintf(`SELECT id, buyer_id, seller_id, status, total_cents, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d`, baseQuery, where, len(args)-1, len(args))
|
listQuery := fmt.Sprintf(`SELECT id, buyer_id, seller_id, status, total_cents, payment_method, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, created_at, updated_at %s%s ORDER BY created_at DESC LIMIT $%d OFFSET $%d`, baseQuery, where, len(args)-1, len(args))
|
||||||
|
|
||||||
var rows []struct {
|
var rows []struct {
|
||||||
ID uuid.UUID `db:"id"`
|
ID uuid.UUID `db:"id"`
|
||||||
BuyerID uuid.UUID `db:"buyer_id"`
|
BuyerID uuid.UUID `db:"buyer_id"`
|
||||||
SellerID uuid.UUID `db:"seller_id"`
|
SellerID uuid.UUID `db:"seller_id"`
|
||||||
Status domain.OrderStatus `db:"status"`
|
Status domain.OrderStatus `db:"status"`
|
||||||
TotalCents int64 `db:"total_cents"`
|
TotalCents int64 `db:"total_cents"`
|
||||||
ShippingRecipientName string `db:"shipping_recipient_name"`
|
PaymentMethod domain.PaymentMethod `db:"payment_method"`
|
||||||
ShippingStreet string `db:"shipping_street"`
|
ShippingRecipientName string `db:"shipping_recipient_name"`
|
||||||
ShippingNumber string `db:"shipping_number"`
|
ShippingStreet string `db:"shipping_street"`
|
||||||
ShippingComplement string `db:"shipping_complement"`
|
ShippingNumber string `db:"shipping_number"`
|
||||||
ShippingDistrict string `db:"shipping_district"`
|
ShippingComplement string `db:"shipping_complement"`
|
||||||
ShippingCity string `db:"shipping_city"`
|
ShippingDistrict string `db:"shipping_district"`
|
||||||
ShippingState string `db:"shipping_state"`
|
ShippingCity string `db:"shipping_city"`
|
||||||
ShippingZipCode string `db:"shipping_zip_code"`
|
ShippingState string `db:"shipping_state"`
|
||||||
ShippingCountry string `db:"shipping_country"`
|
ShippingZipCode string `db:"shipping_zip_code"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
ShippingCountry string `db:"shipping_country"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.db.SelectContext(ctx, &rows, listQuery, args...); err != nil {
|
if err := r.db.SelectContext(ctx, &rows, listQuery, args...); err != nil {
|
||||||
|
|
@ -561,12 +562,13 @@ func (r *Repository) ListOrders(ctx context.Context, filter domain.OrderFilter)
|
||||||
return nil, 0, err
|
return nil, 0, err
|
||||||
}
|
}
|
||||||
orders = append(orders, domain.Order{
|
orders = append(orders, domain.Order{
|
||||||
ID: row.ID,
|
ID: row.ID,
|
||||||
BuyerID: row.BuyerID,
|
BuyerID: row.BuyerID,
|
||||||
SellerID: row.SellerID,
|
SellerID: row.SellerID,
|
||||||
Status: row.Status,
|
Status: row.Status,
|
||||||
TotalCents: row.TotalCents,
|
TotalCents: row.TotalCents,
|
||||||
Items: items,
|
PaymentMethod: row.PaymentMethod,
|
||||||
|
Items: items,
|
||||||
Shipping: domain.ShippingAddress{
|
Shipping: domain.ShippingAddress{
|
||||||
RecipientName: row.ShippingRecipientName,
|
RecipientName: row.ShippingRecipientName,
|
||||||
Street: row.ShippingStreet,
|
Street: row.ShippingStreet,
|
||||||
|
|
@ -587,24 +589,25 @@ func (r *Repository) ListOrders(ctx context.Context, filter domain.OrderFilter)
|
||||||
|
|
||||||
func (r *Repository) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
|
func (r *Repository) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order, error) {
|
||||||
var row struct {
|
var row struct {
|
||||||
ID uuid.UUID `db:"id"`
|
ID uuid.UUID `db:"id"`
|
||||||
BuyerID uuid.UUID `db:"buyer_id"`
|
BuyerID uuid.UUID `db:"buyer_id"`
|
||||||
SellerID uuid.UUID `db:"seller_id"`
|
SellerID uuid.UUID `db:"seller_id"`
|
||||||
Status domain.OrderStatus `db:"status"`
|
Status domain.OrderStatus `db:"status"`
|
||||||
TotalCents int64 `db:"total_cents"`
|
TotalCents int64 `db:"total_cents"`
|
||||||
ShippingRecipientName string `db:"shipping_recipient_name"`
|
PaymentMethod domain.PaymentMethod `db:"payment_method"`
|
||||||
ShippingStreet string `db:"shipping_street"`
|
ShippingRecipientName string `db:"shipping_recipient_name"`
|
||||||
ShippingNumber string `db:"shipping_number"`
|
ShippingStreet string `db:"shipping_street"`
|
||||||
ShippingComplement string `db:"shipping_complement"`
|
ShippingNumber string `db:"shipping_number"`
|
||||||
ShippingDistrict string `db:"shipping_district"`
|
ShippingComplement string `db:"shipping_complement"`
|
||||||
ShippingCity string `db:"shipping_city"`
|
ShippingDistrict string `db:"shipping_district"`
|
||||||
ShippingState string `db:"shipping_state"`
|
ShippingCity string `db:"shipping_city"`
|
||||||
ShippingZipCode string `db:"shipping_zip_code"`
|
ShippingState string `db:"shipping_state"`
|
||||||
ShippingCountry string `db:"shipping_country"`
|
ShippingZipCode string `db:"shipping_zip_code"`
|
||||||
CreatedAt time.Time `db:"created_at"`
|
ShippingCountry string `db:"shipping_country"`
|
||||||
UpdatedAt time.Time `db:"updated_at"`
|
CreatedAt time.Time `db:"created_at"`
|
||||||
|
UpdatedAt time.Time `db:"updated_at"`
|
||||||
}
|
}
|
||||||
orderQuery := `SELECT id, buyer_id, seller_id, status, total_cents, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, created_at, updated_at FROM orders WHERE id = $1`
|
orderQuery := `SELECT id, buyer_id, seller_id, status, total_cents, payment_method, COALESCE(shipping_recipient_name, '') as shipping_recipient_name, COALESCE(shipping_street, '') as shipping_street, COALESCE(shipping_number, '') as shipping_number, COALESCE(shipping_complement, '') as shipping_complement, COALESCE(shipping_district, '') as shipping_district, COALESCE(shipping_city, '') as shipping_city, COALESCE(shipping_state, '') as shipping_state, COALESCE(shipping_zip_code, '') as shipping_zip_code, COALESCE(shipping_country, '') as shipping_country, created_at, updated_at FROM orders WHERE id = $1`
|
||||||
if err := r.db.GetContext(ctx, &row, orderQuery, id); err != nil {
|
if err := r.db.GetContext(ctx, &row, orderQuery, id); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
@ -615,12 +618,13 @@ func (r *Repository) GetOrder(ctx context.Context, id uuid.UUID) (*domain.Order,
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: row.ID,
|
ID: row.ID,
|
||||||
BuyerID: row.BuyerID,
|
BuyerID: row.BuyerID,
|
||||||
SellerID: row.SellerID,
|
SellerID: row.SellerID,
|
||||||
Status: row.Status,
|
Status: row.Status,
|
||||||
TotalCents: row.TotalCents,
|
TotalCents: row.TotalCents,
|
||||||
Items: items,
|
PaymentMethod: row.PaymentMethod,
|
||||||
|
Items: items,
|
||||||
Shipping: domain.ShippingAddress{
|
Shipping: domain.ShippingAddress{
|
||||||
RecipientName: row.ShippingRecipientName,
|
RecipientName: row.ShippingRecipientName,
|
||||||
Street: row.ShippingStreet,
|
Street: row.ShippingStreet,
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@ func TestCreateCompany(t *testing.T) {
|
||||||
defer repo.db.Close()
|
defer repo.db.Close()
|
||||||
|
|
||||||
company := &domain.Company{
|
company := &domain.Company{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
CNPJ: "12345678901234",
|
CNPJ: "12345678901234",
|
||||||
CorporateName: "Test Pharmacy",
|
CorporateName: "Test Pharmacy",
|
||||||
Category: "farmacia",
|
Category: "farmacia",
|
||||||
|
|
@ -72,7 +72,7 @@ func TestGetCompany(t *testing.T) {
|
||||||
repo, mock := newMockRepo(t)
|
repo, mock := newMockRepo(t)
|
||||||
defer repo.db.Close()
|
defer repo.db.Close()
|
||||||
|
|
||||||
id := uuid.Must(uuid.NewV4())
|
id := uuid.Must(uuid.NewV7())
|
||||||
|
|
||||||
rows := sqlmock.NewRows([]string{"id", "cnpj", "corporate_name", "category", "license_number", "is_verified", "latitude", "longitude", "city", "state", "created_at", "updated_at"}).
|
rows := sqlmock.NewRows([]string{"id", "cnpj", "corporate_name", "category", "license_number", "is_verified", "latitude", "longitude", "city", "state", "created_at", "updated_at"}).
|
||||||
AddRow(id, "123", "Test", "farmacia", "123", false, 0.0, 0.0, "City", "ST", time.Now(), time.Now())
|
AddRow(id, "123", "Test", "farmacia", "123", false, 0.0, 0.0, "City", "ST", time.Now(), time.Now())
|
||||||
|
|
@ -97,8 +97,8 @@ func TestCreateProduct(t *testing.T) {
|
||||||
defer repo.db.Close()
|
defer repo.db.Close()
|
||||||
|
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
Description: "Desc",
|
Description: "Desc",
|
||||||
Batch: "B1",
|
Batch: "B1",
|
||||||
|
|
@ -130,7 +130,7 @@ func TestListProducts(t *testing.T) {
|
||||||
repo, mock := newMockRepo(t)
|
repo, mock := newMockRepo(t)
|
||||||
defer repo.db.Close()
|
defer repo.db.Close()
|
||||||
|
|
||||||
rows := sqlmock.NewRows([]string{"id", "name"}).AddRow(uuid.Must(uuid.NewV4()), "P1")
|
rows := sqlmock.NewRows([]string{"id", "name"}).AddRow(uuid.Must(uuid.NewV7()), "P1")
|
||||||
|
|
||||||
// We expect two queries: count and select list
|
// We expect two queries: count and select list
|
||||||
mock.ExpectQuery(`SELECT count\(\*\) FROM products`).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
mock.ExpectQuery(`SELECT count\(\*\) FROM products`).WillReturnRows(sqlmock.NewRows([]string{"count"}).AddRow(1))
|
||||||
|
|
|
||||||
|
|
@ -379,7 +379,7 @@ func TestGetCompany(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
company := &domain.Company{
|
company := &domain.Company{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Category: "farmacia",
|
Category: "farmacia",
|
||||||
CNPJ: "12345678901234",
|
CNPJ: "12345678901234",
|
||||||
CorporateName: "Test Pharmacy",
|
CorporateName: "Test Pharmacy",
|
||||||
|
|
@ -400,7 +400,7 @@ func TestUpdateCompany(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
company := &domain.Company{
|
company := &domain.Company{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Category: "farmacia",
|
Category: "farmacia",
|
||||||
CNPJ: "12345678901234",
|
CNPJ: "12345678901234",
|
||||||
CorporateName: "Test Pharmacy",
|
CorporateName: "Test Pharmacy",
|
||||||
|
|
@ -423,7 +423,7 @@ func TestDeleteCompany(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
company := &domain.Company{
|
company := &domain.Company{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
CorporateName: "Test Pharmacy",
|
CorporateName: "Test Pharmacy",
|
||||||
}
|
}
|
||||||
repo.companies = append(repo.companies, *company)
|
repo.companies = append(repo.companies, *company)
|
||||||
|
|
@ -443,7 +443,7 @@ func TestVerifyCompany(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
company := &domain.Company{
|
company := &domain.Company{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Category: "farmacia",
|
Category: "farmacia",
|
||||||
CNPJ: "12345678901234",
|
CNPJ: "12345678901234",
|
||||||
CorporateName: "Test Pharmacy",
|
CorporateName: "Test Pharmacy",
|
||||||
|
|
@ -468,7 +468,7 @@ func TestRegisterProduct(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
Description: "A test product",
|
Description: "A test product",
|
||||||
Batch: "BATCH-001",
|
Batch: "BATCH-001",
|
||||||
|
|
@ -506,7 +506,7 @@ func TestGetProduct(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
}
|
}
|
||||||
repo.products = append(repo.products, *product)
|
repo.products = append(repo.products, *product)
|
||||||
|
|
@ -525,7 +525,7 @@ func TestUpdateProduct(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
}
|
}
|
||||||
repo.products = append(repo.products, *product)
|
repo.products = append(repo.products, *product)
|
||||||
|
|
@ -546,7 +546,7 @@ func TestDeleteProduct(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
}
|
}
|
||||||
repo.products = append(repo.products, *product)
|
repo.products = append(repo.products, *product)
|
||||||
|
|
@ -592,7 +592,7 @@ func TestListInventory(t *testing.T) {
|
||||||
func TestAdjustInventory(t *testing.T) {
|
func TestAdjustInventory(t *testing.T) {
|
||||||
svc, _ := newTestService()
|
svc, _ := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
productID := uuid.Must(uuid.NewV4())
|
productID := uuid.Must(uuid.NewV7())
|
||||||
|
|
||||||
item, err := svc.AdjustInventory(ctx, productID, 10, "Restock")
|
item, err := svc.AdjustInventory(ctx, productID, 10, "Restock")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -611,8 +611,8 @@ func TestCreateOrder(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
BuyerID: uuid.Must(uuid.NewV4()),
|
BuyerID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -634,9 +634,9 @@ func TestUpdateOrderStatus(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: uuid.Must(uuid.NewV4()),
|
BuyerID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Status: domain.OrderStatusPending,
|
Status: domain.OrderStatusPending,
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
|
|
@ -655,7 +655,7 @@ func TestCreateUser(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
user := &domain.User{
|
user := &domain.User{
|
||||||
CompanyID: uuid.Must(uuid.NewV4()),
|
CompanyID: uuid.Must(uuid.NewV7()),
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
Name: "Test User",
|
Name: "Test User",
|
||||||
Email: "test@example.com",
|
Email: "test@example.com",
|
||||||
|
|
@ -722,7 +722,7 @@ func TestAuthenticate(t *testing.T) {
|
||||||
|
|
||||||
// First create a user
|
// First create a user
|
||||||
user := &domain.User{
|
user := &domain.User{
|
||||||
CompanyID: uuid.Must(uuid.NewV4()),
|
CompanyID: uuid.Must(uuid.NewV7()),
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
Name: "Test User",
|
Name: "Test User",
|
||||||
Username: "authuser",
|
Username: "authuser",
|
||||||
|
|
@ -755,7 +755,7 @@ func TestAuthenticateInvalidPassword(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
user := &domain.User{
|
user := &domain.User{
|
||||||
CompanyID: uuid.Must(uuid.NewV4()),
|
CompanyID: uuid.Must(uuid.NewV7()),
|
||||||
Role: "admin",
|
Role: "admin",
|
||||||
Name: "Test User",
|
Name: "Test User",
|
||||||
Username: "failuser",
|
Username: "failuser",
|
||||||
|
|
@ -776,10 +776,10 @@ func TestAddItemToCart(t *testing.T) {
|
||||||
svc, repo := newTestService()
|
svc, repo := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
buyerID := uuid.Must(uuid.NewV4())
|
buyerID := uuid.Must(uuid.NewV7())
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Test Product",
|
Name: "Test Product",
|
||||||
PriceCents: 1000,
|
PriceCents: 1000,
|
||||||
Stock: 100,
|
Stock: 100,
|
||||||
|
|
@ -802,8 +802,8 @@ func TestAddItemToCartInvalidQuantity(t *testing.T) {
|
||||||
svc, _ := newTestService()
|
svc, _ := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
buyerID := uuid.Must(uuid.NewV4())
|
buyerID := uuid.Must(uuid.NewV7())
|
||||||
productID := uuid.Must(uuid.NewV4())
|
productID := uuid.Must(uuid.NewV7())
|
||||||
|
|
||||||
_, err := svc.AddItemToCart(ctx, buyerID, productID, 0)
|
_, err := svc.AddItemToCart(ctx, buyerID, productID, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -815,10 +815,10 @@ func TestCartB2BDiscount(t *testing.T) {
|
||||||
svc, repo := newTestService()
|
svc, repo := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
buyerID := uuid.Must(uuid.NewV4())
|
buyerID := uuid.Must(uuid.NewV7())
|
||||||
product := &domain.Product{
|
product := &domain.Product{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Name: "Expensive Product",
|
Name: "Expensive Product",
|
||||||
PriceCents: 50000, // R$500 per unit
|
PriceCents: 50000, // R$500 per unit
|
||||||
Stock: 1000,
|
Stock: 1000,
|
||||||
|
|
@ -847,11 +847,11 @@ func TestCreateReview(t *testing.T) {
|
||||||
svc, repo := newTestService()
|
svc, repo := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
buyerID := uuid.Must(uuid.NewV4())
|
buyerID := uuid.Must(uuid.NewV7())
|
||||||
sellerID := uuid.Must(uuid.NewV4())
|
sellerID := uuid.Must(uuid.NewV7())
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: buyerID,
|
BuyerID: buyerID,
|
||||||
SellerID: sellerID,
|
SellerID: sellerID,
|
||||||
Status: domain.OrderStatusDelivered,
|
Status: domain.OrderStatusDelivered,
|
||||||
|
|
@ -873,7 +873,7 @@ func TestCreateReviewInvalidRating(t *testing.T) {
|
||||||
svc, _ := newTestService()
|
svc, _ := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
_, err := svc.CreateReview(ctx, uuid.Must(uuid.NewV4()), uuid.Must(uuid.NewV4()), 6, "Invalid")
|
_, err := svc.CreateReview(ctx, uuid.Must(uuid.NewV7()), uuid.Must(uuid.NewV7()), 6, "Invalid")
|
||||||
if err == nil {
|
if err == nil {
|
||||||
t.Error("expected error for invalid rating")
|
t.Error("expected error for invalid rating")
|
||||||
}
|
}
|
||||||
|
|
@ -883,9 +883,9 @@ func TestCreateReviewNotDelivered(t *testing.T) {
|
||||||
svc, repo := newTestService()
|
svc, repo := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
buyerID := uuid.Must(uuid.NewV4())
|
buyerID := uuid.Must(uuid.NewV7())
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: buyerID,
|
BuyerID: buyerID,
|
||||||
Status: domain.OrderStatusPending, // Not delivered
|
Status: domain.OrderStatusPending, // Not delivered
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
|
|
@ -904,7 +904,7 @@ func TestGetSellerDashboard(t *testing.T) {
|
||||||
svc, _ := newTestService()
|
svc, _ := newTestService()
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
sellerID := uuid.Must(uuid.NewV4())
|
sellerID := uuid.Must(uuid.NewV7())
|
||||||
dashboard, err := svc.GetSellerDashboard(ctx, sellerID)
|
dashboard, err := svc.GetSellerDashboard(ctx, sellerID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed to get seller dashboard: %v", err)
|
t.Fatalf("failed to get seller dashboard: %v", err)
|
||||||
|
|
@ -936,9 +936,9 @@ func TestCreatePaymentPreference(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: uuid.Must(uuid.NewV4()),
|
BuyerID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
repo.orders = append(repo.orders, *order)
|
repo.orders = append(repo.orders, *order)
|
||||||
|
|
@ -961,9 +961,9 @@ func TestHandlePaymentWebhook(t *testing.T) {
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
order := &domain.Order{
|
order := &domain.Order{
|
||||||
ID: uuid.Must(uuid.NewV4()),
|
ID: uuid.Must(uuid.NewV7()),
|
||||||
BuyerID: uuid.Must(uuid.NewV4()),
|
BuyerID: uuid.Must(uuid.NewV7()),
|
||||||
SellerID: uuid.Must(uuid.NewV4()),
|
SellerID: uuid.Must(uuid.NewV7()),
|
||||||
Status: domain.OrderStatusPending,
|
Status: domain.OrderStatusPending,
|
||||||
TotalCents: 10000,
|
TotalCents: 10000,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
import { useState } from 'react'
|
import { useState, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Shell } from '../layouts/Shell'
|
import { Shell } from '../layouts/Shell'
|
||||||
import { useCartStore, selectGroupedCart, selectCartSummary } from '../stores/cartStore'
|
import { useCartStore, selectGroupedCart, selectCartSummary } from '../stores/cartStore'
|
||||||
import { useAuth } from '../context/AuthContext'
|
import { useAuth } from '../context/AuthContext'
|
||||||
import { ordersService, CreateOrderRequest } from '../services/ordersService'
|
import { ordersService, CreateOrderRequest } from '../services/ordersService'
|
||||||
|
import { shippingService } from '../services/shippingService'
|
||||||
|
import { apiClient } from '../services/apiClient'
|
||||||
import { formatCurrency } from '../utils/format'
|
import { formatCurrency } from '../utils/format'
|
||||||
import { ArrowLeft, CheckCircle2, Truck } from 'lucide-react'
|
import { ArrowLeft, CheckCircle2, Truck, Store } from 'lucide-react'
|
||||||
|
|
||||||
export function CheckoutPage() {
|
export function CheckoutPage() {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
|
@ -15,6 +17,10 @@ export function CheckoutPage() {
|
||||||
const clearAll = useCartStore((state) => state.clearAll)
|
const clearAll = useCartStore((state) => state.clearAll)
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
|
||||||
|
// Shipping options state
|
||||||
|
const [shippingOptions, setShippingOptions] = useState<Record<string, { delivery: boolean, pickup: boolean, pickupAddress: string, price: number }>>({})
|
||||||
|
|
||||||
const [shipping, setShipping] = useState({
|
const [shipping, setShipping] = useState({
|
||||||
recipient_name: user?.name || '',
|
recipient_name: user?.name || '',
|
||||||
street: '',
|
street: '',
|
||||||
|
|
@ -27,8 +33,65 @@ export function CheckoutPage() {
|
||||||
country: 'Brasil'
|
country: 'Brasil'
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Pre-fill address from company
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchCompanyAddress() {
|
||||||
|
try {
|
||||||
|
// TODO: Use a proper service for this
|
||||||
|
const res = await apiClient.get<any>('/v1/companies/me')
|
||||||
|
const company = res.data
|
||||||
|
if (company) {
|
||||||
|
setShipping(prev => ({
|
||||||
|
...prev,
|
||||||
|
street: company.street || '', // Wait, company model fields differ from shipping fields?
|
||||||
|
// Let's check company model in backend: city, state, etc.
|
||||||
|
// Actually, the backend Company model has: city, state, latitude, longitude.
|
||||||
|
// It seems it does NOT have street, number, district, zip_code separately in the main table?
|
||||||
|
// Let's re-read models.go.
|
||||||
|
// Ah, lines 18-21: Latitude, Longitude, City, State.
|
||||||
|
// It seems we lack detailed address fields in Company model for pre-filling!
|
||||||
|
// Wait, seeder line 113: city, state.
|
||||||
|
// We might only be able to pre-fill city/state for now unless I missed something.
|
||||||
|
city: company.city || '',
|
||||||
|
state: company.state || '',
|
||||||
|
// We can't pre-fill street/number if they aren't there.
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Failed to fetch company address", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (user) fetchCompanyAddress()
|
||||||
|
}, [user])
|
||||||
|
|
||||||
|
// Fetch shipping settings for sellers
|
||||||
|
useEffect(() => {
|
||||||
|
async function fetchShipping() {
|
||||||
|
const options: Record<string, any> = {}
|
||||||
|
for (const sellerId of Object.keys(groups)) {
|
||||||
|
try {
|
||||||
|
const settings = await shippingService.getSettings(sellerId)
|
||||||
|
options[sellerId] = {
|
||||||
|
delivery: settings.active,
|
||||||
|
pickup: settings.pickup_active,
|
||||||
|
pickupAddress: settings.pickup_address,
|
||||||
|
price: 15.00 // Mock calc for now, or implement calculateShipping
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(`Failed to fetch shipping for ${sellerId}`, e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setShippingOptions(options)
|
||||||
|
}
|
||||||
|
if (Object.keys(groups).length > 0) fetchShipping()
|
||||||
|
}, [groups])
|
||||||
|
|
||||||
|
// Payment method selection
|
||||||
|
const [paymentMethod, setPaymentMethod] = useState<'pix' | 'credit_card' | 'debit_card'>('credit_card')
|
||||||
|
|
||||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const { name, value } = e.target
|
const { name, value } = e.target
|
||||||
|
setPaymentMethod(prev => prev) // Keep state if needed, though strictly not needed for this simple setter
|
||||||
setShipping(prev => ({ ...prev, [name]: value }))
|
setShipping(prev => ({ ...prev, [name]: value }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -59,7 +122,8 @@ export function CheckoutPage() {
|
||||||
state: shipping.state,
|
state: shipping.state,
|
||||||
zip_code: shipping.zip_code,
|
zip_code: shipping.zip_code,
|
||||||
country: shipping.country
|
country: shipping.country
|
||||||
}
|
},
|
||||||
|
payment_method: paymentMethod
|
||||||
}
|
}
|
||||||
return ordersService.createOrder(orderData)
|
return ordersService.createOrder(orderData)
|
||||||
})
|
})
|
||||||
|
|
@ -192,13 +256,58 @@ export function CheckoutPage() {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Payment Method Stub */}
|
{/* Payment Method Selection */}
|
||||||
<div className="rounded-lg bg-white p-6 shadow-sm">
|
<div className="rounded-lg bg-white p-6 shadow-sm">
|
||||||
<h2 className="text-lg font-semibold text-gray-800">Pagamento</h2>
|
<div className="mb-4 flex items-center gap-2">
|
||||||
<p className="mt-2 text-sm text-gray-600">Este é um ambiente de demonstração. O pagamento será processado como "Confirmado" para fins de teste.</p>
|
<CheckCircle2 className="h-5 w-5 text-medicalBlue" />
|
||||||
<div className="mt-4 flex items-center gap-3 rounded-lg border border-green-200 bg-green-50 p-4">
|
<h2 className="text-lg font-semibold text-gray-800">Forma de Pagamento</h2>
|
||||||
<CheckCircle2 className="h-5 w-5 text-green-600" />
|
</div>
|
||||||
<span className="text-sm font-medium text-green-800">Método de Teste (Aprovação Automática)</span>
|
|
||||||
|
<div className="space-y-3">
|
||||||
|
<label className={`flex cursor-pointer items-center rounded-lg border p-4 transition-colors ${paymentMethod === 'pix' ? 'border-medicalBlue bg-blue-50' : 'border-gray-200 hover:bg-gray-50'}`}>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="paymentMethod"
|
||||||
|
value="pix"
|
||||||
|
checked={paymentMethod === 'pix'}
|
||||||
|
onChange={() => setPaymentMethod('pix')}
|
||||||
|
className="h-4 w-4 border-gray-300 text-medicalBlue focus:ring-medicalBlue"
|
||||||
|
/>
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block text-sm font-medium text-gray-900">Pix</span>
|
||||||
|
<span className="block text-sm text-gray-500">Aprovação imediata</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className={`flex cursor-pointer items-center rounded-lg border p-4 transition-colors ${paymentMethod === 'credit_card' ? 'border-medicalBlue bg-blue-50' : 'border-gray-200 hover:bg-gray-50'}`}>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="paymentMethod"
|
||||||
|
value="credit_card"
|
||||||
|
checked={paymentMethod === 'credit_card'}
|
||||||
|
onChange={() => setPaymentMethod('credit_card')}
|
||||||
|
className="h-4 w-4 border-gray-300 text-medicalBlue focus:ring-medicalBlue"
|
||||||
|
/>
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block text-sm font-medium text-gray-900">Cartão de Crédito</span>
|
||||||
|
<span className="block text-sm text-gray-500">Em até 12x</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label className={`flex cursor-pointer items-center rounded-lg border p-4 transition-colors ${paymentMethod === 'debit_card' ? 'border-medicalBlue bg-blue-50' : 'border-gray-200 hover:bg-gray-50'}`}>
|
||||||
|
<input
|
||||||
|
type="radio"
|
||||||
|
name="paymentMethod"
|
||||||
|
value="debit_card"
|
||||||
|
checked={paymentMethod === 'debit_card'}
|
||||||
|
onChange={() => setPaymentMethod('debit_card')}
|
||||||
|
className="h-4 w-4 border-gray-300 text-medicalBlue focus:ring-medicalBlue"
|
||||||
|
/>
|
||||||
|
<div className="ml-3">
|
||||||
|
<span className="block text-sm font-medium text-gray-900">Cartão de Débito</span>
|
||||||
|
<span className="block text-sm text-gray-500">Pagamento à vista</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -211,12 +320,44 @@ export function CheckoutPage() {
|
||||||
{Object.entries(groups).map(([vendorId, group]) => (
|
{Object.entries(groups).map(([vendorId, group]) => (
|
||||||
<div key={vendorId} className="border-b border-gray-100 pb-4 last:border-0 last:pb-0">
|
<div key={vendorId} className="border-b border-gray-100 pb-4 last:border-0 last:pb-0">
|
||||||
<p className="mb-2 text-sm font-medium text-gray-600">{group.vendorName}</p>
|
<p className="mb-2 text-sm font-medium text-gray-600">{group.vendorName}</p>
|
||||||
{group.items.map(item => (
|
<div key={item.id} className="flex justify-between text-sm">
|
||||||
<div key={item.id} className="flex justify-between text-sm">
|
<span className="text-gray-800">{item.quantity}x {item.name}</span>
|
||||||
<span className="text-gray-800">{item.quantity}x {item.name}</span>
|
<span className="text-gray-600">R$ {formatCurrency(item.quantity * item.unitPrice)}</span>
|
||||||
<span className="text-gray-600">R$ {formatCurrency(item.quantity * item.unitPrice)}</span>
|
</div>
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
{/* Shipping Options for this Vendor */}
|
||||||
|
{shippingOptions[vendorId] && (
|
||||||
|
<div className="mt-3 rounded-md bg-gray-50 p-3">
|
||||||
|
<p className="mb-2 text-xs font-semibold text-gray-500 uppercase">Entrega</p>
|
||||||
|
<div className="flex flex-col gap-2">
|
||||||
|
{shippingOptions[vendorId].delivery ? (
|
||||||
|
<label className="flex cursor-pointer items-center justify-between text-sm">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input type="radio" name={`shipping-${vendorId}`} className="mr-2 text-medicalBlue focus:ring-medicalBlue" defaultChecked />
|
||||||
|
<span>Entrega Padrão</span>
|
||||||
|
</div>
|
||||||
|
<span className="font-medium text-gray-900">R$ {formatCurrency(shippingOptions[vendorId].price * 100)}</span>
|
||||||
|
</label>
|
||||||
|
) : (
|
||||||
|
<div className="text-sm text-red-500">Entrega indisponível para esta região</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{shippingOptions[vendorId].pickup && (
|
||||||
|
<label className="flex cursor-pointer items-center justify-between text-sm">
|
||||||
|
<div className="flex items-center">
|
||||||
|
<input type="radio" name={`shipping-${vendorId}`} className="mr-2 text-medicalBlue focus:ring-medicalBlue" />
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span>Retirada na Loja</span>
|
||||||
|
<span className="text-xs text-gray-500">{shippingOptions[vendorId].pickupAddress}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="font-medium text-green-600">Grátis</span>
|
||||||
|
</label>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
|
@ -225,6 +366,7 @@ export function CheckoutPage() {
|
||||||
<span>Total</span>
|
<span>Total</span>
|
||||||
<span className="text-xl text-medicalBlue">R$ {formatCurrency(summary.totalValue)}</span>
|
<span className="text-xl text-medicalBlue">R$ {formatCurrency(summary.totalValue)}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<p className="mt-1 text-xs text-gray-500 text-right">Taxas de entrega não incluídas no total acima</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ export interface CreateOrderRequest {
|
||||||
seller_id: string
|
seller_id: string
|
||||||
items: OrderItem[]
|
items: OrderItem[]
|
||||||
shipping: ShippingAddress
|
shipping: ShippingAddress
|
||||||
|
payment_method: 'pix' | 'credit_card' | 'debit_card'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ordersService = {
|
export const ordersService = {
|
||||||
|
|
|
||||||
38
marketplace/src/services/shippingService.ts
Normal file
38
marketplace/src/services/shippingService.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
|
||||||
|
import { apiClient } from './apiClient'
|
||||||
|
import { ShippingSettings } from '../types/shipping'
|
||||||
|
|
||||||
|
export interface CalculateShippingRequest {
|
||||||
|
buyer_id: string
|
||||||
|
order_total_cents: number
|
||||||
|
items: {
|
||||||
|
seller_id: string
|
||||||
|
product_id: string
|
||||||
|
quantity: number
|
||||||
|
price_cents: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CalculateShippingResponse {
|
||||||
|
options: {
|
||||||
|
seller_id: string
|
||||||
|
delivery_fee_cents: number
|
||||||
|
distance_km: number
|
||||||
|
estimated_days: number
|
||||||
|
pickup_available: boolean
|
||||||
|
pickup_address?: string
|
||||||
|
pickup_hours?: string
|
||||||
|
}[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export const shippingService = {
|
||||||
|
getSettings: async (vendorId: string) => {
|
||||||
|
const response = await apiClient.get<ShippingSettings>(`/v1/shipping/settings/${vendorId}`)
|
||||||
|
return response
|
||||||
|
},
|
||||||
|
|
||||||
|
calculate: async (data: CalculateShippingRequest) => {
|
||||||
|
const response = await apiClient.post<CalculateShippingResponse>('/v1/shipping/calculate', data)
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
}
|
||||||
14
marketplace/src/types/shipping.ts
Normal file
14
marketplace/src/types/shipping.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
export interface ShippingSettings {
|
||||||
|
vendor_id: string
|
||||||
|
active: boolean
|
||||||
|
max_radius_km: number
|
||||||
|
price_per_km_cents: number
|
||||||
|
min_fee_cents: number
|
||||||
|
free_shipping_threshold_cents?: number
|
||||||
|
pickup_active: boolean
|
||||||
|
pickup_address: string
|
||||||
|
pickup_hours: string
|
||||||
|
latitude: number
|
||||||
|
longitude: number
|
||||||
|
}
|
||||||
|
|
@ -90,6 +90,7 @@ func SeedLean(dsn string) (string, error) {
|
||||||
log.Println("🧹 [Lean] Resetting database...")
|
log.Println("🧹 [Lean] Resetting database...")
|
||||||
|
|
||||||
// Re-create tables
|
// Re-create tables
|
||||||
|
mustExec(db, `DROP TABLE IF EXISTS shipping_settings CASCADE`)
|
||||||
mustExec(db, `DROP TABLE IF EXISTS shipments CASCADE`)
|
mustExec(db, `DROP TABLE IF EXISTS shipments CASCADE`)
|
||||||
mustExec(db, `DROP TABLE IF EXISTS inventory_adjustments CASCADE`)
|
mustExec(db, `DROP TABLE IF EXISTS inventory_adjustments CASCADE`)
|
||||||
mustExec(db, `DROP TABLE IF EXISTS order_items CASCADE`)
|
mustExec(db, `DROP TABLE IF EXISTS order_items CASCADE`)
|
||||||
|
|
@ -155,6 +156,7 @@ func SeedLean(dsn string) (string, error) {
|
||||||
buyer_id UUID NOT NULL REFERENCES companies(id),
|
buyer_id UUID NOT NULL REFERENCES companies(id),
|
||||||
seller_id UUID NOT NULL REFERENCES companies(id),
|
seller_id UUID NOT NULL REFERENCES companies(id),
|
||||||
status TEXT NOT NULL,
|
status TEXT NOT NULL,
|
||||||
|
payment_method TEXT NOT NULL DEFAULT 'credit_card',
|
||||||
total_cents BIGINT NOT NULL,
|
total_cents BIGINT NOT NULL,
|
||||||
shipping_recipient_name TEXT,
|
shipping_recipient_name TEXT,
|
||||||
shipping_street TEXT,
|
shipping_street TEXT,
|
||||||
|
|
@ -213,6 +215,22 @@ func SeedLean(dsn string) (string, error) {
|
||||||
updated_at TIMESTAMPTZ NOT NULL
|
updated_at TIMESTAMPTZ NOT NULL
|
||||||
)`)
|
)`)
|
||||||
|
|
||||||
|
mustExec(db, `CREATE TABLE shipping_settings (
|
||||||
|
vendor_id UUID PRIMARY KEY REFERENCES companies(id),
|
||||||
|
active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
max_radius_km DOUBLE PRECISION NOT NULL DEFAULT 20.0,
|
||||||
|
min_fee_cents BIGINT NOT NULL DEFAULT 500,
|
||||||
|
price_per_km_cents BIGINT NOT NULL DEFAULT 100,
|
||||||
|
free_shipping_threshold_cents BIGINT,
|
||||||
|
pickup_active BOOLEAN NOT NULL DEFAULT TRUE,
|
||||||
|
pickup_address TEXT NOT NULL,
|
||||||
|
pickup_hours TEXT NOT NULL,
|
||||||
|
latitude DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||||
|
longitude DOUBLE PRECISION NOT NULL DEFAULT 0,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL,
|
||||||
|
updated_at TIMESTAMPTZ NOT NULL
|
||||||
|
)`)
|
||||||
|
|
||||||
// Helper for hashing
|
// Helper for hashing
|
||||||
hashPwd := func(pwd string) string {
|
hashPwd := func(pwd string) string {
|
||||||
h, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
h, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
||||||
|
|
@ -276,6 +294,17 @@ func SeedLean(dsn string) (string, error) {
|
||||||
return "", fmt.Errorf("create library %s: %v", ph.Name, err)
|
return "", fmt.Errorf("create library %s: %v", ph.Name, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 1.1 Create Shipping Settings
|
||||||
|
address := fmt.Sprintf("Rua Exemplo %s, Anápolis - GO", ph.Suffix)
|
||||||
|
_, err = db.ExecContext(ctx, `
|
||||||
|
INSERT INTO shipping_settings (vendor_id, active, max_radius_km, min_fee_cents, price_per_km_cents, free_shipping_threshold_cents, pickup_active, pickup_address, pickup_hours, latitude, longitude, created_at, updated_at)
|
||||||
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13)`,
|
||||||
|
companyID, true, 25.0, 500, 200, 15000, true, address, "Seg-Sex: 08:00-18:00", ph.Lat, ph.Lng, now, now,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("⚠️ Failed to create shipping settings for %s: %v", ph.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Create Users (Dono, Colab, Entregador)
|
// 2. Create Users (Dono, Colab, Entregador)
|
||||||
roles := []struct {
|
roles := []struct {
|
||||||
Role string
|
Role string
|
||||||
|
|
@ -570,7 +599,7 @@ func generateTenants(rng *rand.Rand, count int) []map[string]interface{} {
|
||||||
tenants := make([]map[string]interface{}, 0, count)
|
tenants := make([]map[string]interface{}, 0, count)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
name := fmt.Sprintf("%s %d", pharmacyNames[rng.Intn(len(pharmacyNames))], i+1)
|
name := fmt.Sprintf("%s %d", pharmacyNames[rng.Intn(len(pharmacyNames))], i+1)
|
||||||
cnpj := generateCNPJ(rng)
|
cnpj := generateCNPJ(rng)
|
||||||
|
|
||||||
|
|
@ -604,7 +633,7 @@ func generateProducts(rng *rand.Rand, sellerID uuid.UUID, count int) []map[strin
|
||||||
products := make([]map[string]interface{}, 0, count)
|
products := make([]map[string]interface{}, 0, count)
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
id, _ := uuid.NewV4()
|
id, _ := uuid.NewV7()
|
||||||
med := medicamentos[rng.Intn(len(medicamentos))]
|
med := medicamentos[rng.Intn(len(medicamentos))]
|
||||||
|
|
||||||
// Random expiration: 30 days to 2 years from now
|
// Random expiration: 30 days to 2 years from now
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue