Compatibilidade

DETALHES TÉCNICOS - Problemas de Compatibilidade

DETALHES TÉCNICOS - Problemas de Compatibilidade

PROBLEMA #1: Campo quantidade vs quantity em Step2

Localização 1: Interface Definition

Arquivo: /apps/producer/components/order/wizard/Step2Products.vueLinha: 21-25

interface ProdutoItem {
  produto: Product | null
  quantidade: number      // ← INTERFACE DECLARA "quantidade"
  aceita_similar: boolean
}

Localização 2: Template Usage

Arquivo: /apps/producer/components/order/wizard/Step2Products.vueLinha: 222

<input
  id="quantidade-${index}"
  v-model.number="item.quantity"  <!--  TEMPLATE USA "quantity" -->
  type="number"
  min="1"
  step="0.01"
  class="form-control text-sm"
  placeholder="Ex: 100"
/>

Localização 3: Synchronization Function

Arquivo: /apps/producer/components/order/wizard/Step2Products.vueLinha: 115-129

const sincronizarFormData = () => {
  formData.value.produtos = produtos.value
    .filter(item => item.produto !== null)
    .map(item => ({
      produtoId: item.produto!.id.toString(),
      produtoNome: item.produto!.commercial_name,
      quantidade: item.quantity,  // ← TENTA ACESSAR "item.quantity"
      unidade: item.produto!.measurement_unit?.sigla || '',
      measurement_unit_id: item.produto!.measurement_unit_id || undefined,
      aceita_similar: item.aceita_similar,
      marca: item.produto!.brand?.description || '',
      principio_ativo: item.produto!.active_ingredient?.description || '',
    }))
}

Expected Flow

  1. Interface declara: ProdutoItem.quantidade: number
  2. Template deve usar: v-model.number="item.quantidade"
  3. Sync envia: { quantidade: item.quantidade }

Actual Flow

  1. Interface declara: ProdutoItem.quantidade: number
  2. Template usa: v-model.number="item.quantity" ← WRONG
  3. Sync envia: { quantidade: item.quantity } ← item.quantity is UNDEFINED

Fix Option A: Rename to "quantity"

// Step 1: Update interface
interface ProdutoItem {
  quantidade: number      // REMOVE
  quantity: number        // ADD
}

// Step 2: Template already correct
v-model.number="item.quantity"  // OK

// Step 3: Sync already correct
quantidade: item.quantity,  // OK

Fix Option B: Rename to "quantidade"

// Step 1: Interface already correct
interface ProdutoItem {
  quantidade: number      // KEEP
}

// Step 2: Update template
v-model.number="item.quantidade"  // CHANGE from "quantity"

// Step 3: Sync already correct
quantidade: item.quantidade,  // CHANGE from "item.quantity"

PROBLEMA #2: Missing API Validation

Affected Endpoint

File: /apps/api/app/Http/Controllers/Api/OrderController.phpMethod: saveDraft() (Line 576)

Current Request Validation

File: /apps/api/app/Http/Requests/StoreOrderRequest.php

public function rules(): array
{
    return [
        // These are validated
        'description' => 'required|string|min:50|max:1000',
        'data_limite_cotacao' => 'required|date|after:+2 days',
        'raio_fornecimento' => 'required|integer|min:10|max:500',
        'items' => 'required|array|min:1',
        'items.*.product_id' => 'required|integer|exists:products,id',
        'items.*.quantidade' => 'required|integer|min:1',
        'items.*.measurement_unit_id' => 'required|integer|exists:measurement_units,id',
        
        // These are NOT validated
        // bidding_type
        // customer_address_id
        // freight_type
        // deadline_mode
        // condicao_pagamento
        // prazo_entrega (delivery_deadline)
        // payment_terms
    ];
}

Missing Validations Needed

1. bidding_type Validation

// Should validate but doesn't
'bidding_type' => 'required|in:open,closed',

Frontend sends: open or closed (Line 19, useWizardOrder.ts) Database expects: Enum (OrderStatus or similar) Current issue: No validation = invalid data could be saved

2. customer_address_id Validation

// Should validate but doesn't
'customer_address_id' => 'required|integer|exists:customer_addresses,id',

Frontend sends: Integer ID (Line 70, Step4Location.vue) Database expects: Must exist in customer_addresses table Current issue: No validation = orphaned addresses could be created

3. freight_type Validation

// Should validate but doesn't
'freight_type' => 'required|in:cif,fob',

Frontend sends: cif, fob, or empty string (Line 43, useWizardOrder.ts) Database expects: Enum value Current issue: Empty string could be saved as valid

4. deadline_mode Validation

// Should validate but doesn't
'deadline_mode' => 'in:general,specific',

Frontend sends: general or specific (Line 37, useWizardOrder.ts) Database expects: Enum Current issue: No validation = invalid mode could be saved

5. condicao_pagamento Validation

// Should validate but doesn't
'condicao_pagamento' => 'in:avista,parcelado,a_combinar',

Frontend sends: Valid values (Line 32-35, Step3Deadlines.vue) Database expects: Valid enum value Current issue: No validation = custom invalid values could be saved

6. prazo_entrega Validation

// Should validate when deadline_mode is 'general'
'prazo_entrega' => 'required_if:deadline_mode,general|integer|min:3|max:60',

Frontend sends: Number of days (Line 39, useWizardOrder.ts) Database expects: Valid days (3-60) Current issue: Frontend validates but API doesn't

public function rules(): array
{
    return [
        // Original validations
        'description' => 'nullable|string|min:50|max:1000',  // Allow null (auto-filled)
        'deadline_date' => 'required|date|after:+2 days',    // FIELD NAME CHECK
        'supply_radius' => 'required|integer|min:10|max:500',

        // Items validation
        'items' => 'array|min:0',  // Allow empty for drafts
        'items.*.product_id' => 'required|integer|exists:products,id',
        'items.*.quantidade' => 'required|integer|min:1',
        'items.*.measurement_unit_id' => 'required|integer|exists:measurement_units,id',
        'items.*.aceita_similar' => 'boolean',
        'items.*.delivery_date' => 'nullable|date|after:deadline_date',

        // NEW: Missing validations
        'bidding_type' => 'in:open,closed',
        'customer_address_id' => 'integer|exists:customer_addresses,id',
        'freight_type' => 'in:cif,fob',
        'deadline_mode' => 'in:general,specific',
        'condicao_pagamento' => 'in:avista,parcelado,a_combinar',
        'prazo_entrega' => 'integer|min:3|max:60',
    ];
}

PROBLEMA #3: Fixed "description" Field

Location

File: /apps/producer/composables/useWizardOrder.tsLine: 393

const convertToApiFormat = (status: 'draft' | 'completed' = 'draft') => {
    return {
        description: 'Rascunho de licitação',  // ← ALWAYS FIXED
        // ... other fields
    }
}

Problem

  1. User cannot customize description
  2. API validates: min:50|max:1000
  3. Fixed text is always the same for all orders

Expected Behavior

Users should be able to write custom description like:

  • "Herbicida para soja - safra 2024"
  • "Adubo NPK para milho"
  • "Sementes de arroz variedade BRS"

Solution

Add field to Step 1:

// WizardFormData interface (add)
export interface WizardFormData {
  // ... existing fields
  descricao: string  // NEW FIELD
}

// useWizardOrder state (add)
formData.value = {
  descricao: '',  // NEW
  // ... other fields
}

// convertToApiFormat (change)
const convertToApiFormat = () => {
  return {
    description: formData.value.descricao || 'Rascunho de licitação',
    // ... other fields
  }
}

// Step 1 validation (add)
const validateStep1 = (): boolean => {
  const novosErros: WizardErros = {}
  
  if (!formData.value.descricao || formData.value.descricao.length < 50) {
    novosErros.descricao = 'Descrição deve ter no mínimo 50 caracteres'
  }
  if (formData.value.descricao.length > 1000) {
    novosErros.descricao = 'Descrição deve ter no máximo 1000 caracteres'
  }
  // ... rest of validation
}

Step1BasicInfo.vue (add)

<!-- After product types selection -->
<div class="form-group">
  <label for="descricao" class="form-label required">
    Descrição da Licitação
  </label>
  <textarea
    id="descricao"
    v-model="formData.descricao"
    class="form-control"
    rows="4"
    placeholder="Descreva o que você está solicitando..."
    maxlength="1000"
  ></textarea>
  <p class="text-xs text-agrsis-neutral-600 mt-1">
    {{ formData.descricao?.length || 0 }}/1000 caracteres
  </p>
  <p v-if="erros.descricao" class="text-sm text-red-600 mt-1">
    {{ erros.descricao }}
  </p>
</div>

PROBLEMA #4: Inconsistent Field Names Between Endpoints

Issue

Frontend and API use different field naming conventions

StoreOrderRequest expects:

'data_limite_cotacao'    // Snake case PT-BR
'raio_fornecimento'      // Snake case PT-BR

saveDraft receives:

'deadline_date'          // Snake case EN
'supply_radius'          // Snake case EN
'bidding_type'           // Snake case EN
'deadline_mode'          // Snake case EN
'prazo_entrega'          // Snake case PT-BR (mixed!)
'condicao_pagamento'     // Snake case PT-BR (mixed!)

Order Model

// Database schema uses:
- bidding_type           // EN
- customer_address_id    // EN
- deadline_date          // EN
- supply_radius          // EN
- deadline_mode          // EN
- delivery_deadline      // EN
- payment_terms          // EN
- freight_type           // EN

Recommendation

Standardize on snake_case English throughout:

  • bidding_type
  • customer_address_id
  • deadline_date
  • supply_radius
  • deadline_mode
  • delivery_deadline
  • payment_terms
  • freight_type

PROBLEMA #5: Status Field Handling

Issue

Frontend sends status, API ignores it

Frontend (useWizardOrder.ts, Line 572)

const apiData = convertToApiFormat('completed')  // Sends status
return {
  status: 'draft' | 'completed',  // Explicitly in convertToApiFormat
  // ... other fields
}

API (OrderController.php, Line 651)

'status' => $this->determineDraftStatus($request),  // Ignores frontend's status!

determineDraftStatus Logic

private function determineDraftStatus(Request $request): OrderStatus
{
    $hasDataLimite = !empty($request->deadline_date);
    $hasItems = $request->has('items') && is_array($request->items) && count($request->items) > 0;

    if ($hasDataLimite && $hasItems) {
        return OrderStatus::PENDING;  // Auto-determined
    }
    return OrderStatus::DRAFT;  // Auto-determined
}

Current Behavior

  • Frontend sends: status = 'completed'
  • API receives: status field
  • API ignores: Calculates its own status
  • Result: Frontend's status is discarded

Recommendation

Either:

Option A: Keep current behavior (auto-determine)

// Frontend shouldn't send status
const convertToApiFormat = () => {
  return {
    // DO NOT include status
    description: 'Rascunho de licitação',
    // ... other fields
  }
}

Option B: Use frontend's status

// API should use frontend's status
'status' => $request->input('status', OrderStatus::DRAFT),

Current approach (A) is fine, just don't send status from frontend.


Testing Checklist

Unit Tests Needed

  • test_quantity_field_is_correctly_mapped() - Verify item.quantity syncs
  • test_description_validation() - Min 50, max 1000 chars
  • test_bidding_type_validation() - Only open/closed
  • test_freight_type_validation() - Only cif/fob
  • test_customer_address_exists() - Address ID must exist

Integration Tests Needed

  • test_complete_order_creation_flow() - All steps → API
  • test_draft_saved_with_partial_data() - Only basic info
  • test_draft_published_requires_complete_data() - All mandatory fields
  • test_invalid_data_rejected() - API rejects invalid inputs

End-to-End Tests Needed

  • Create order with 5 products → Verify quantities saved
  • Submit invalid bidding_type → Verify validation error
  • Create draft without address → Verify allowed
  • Publish draft without items → Verify validation error

Last Updated: 2024-12-22

Copyright © 2026