Criei um Chatbot para me Ajudar a Economizar na Compra de Materiais de Construção
        Introdução
O post de hoje é sobre um projeto pessoal que desenvolvi para atender à necessidade de economizar na construção de uma casa no sítio dos meus pais, localizado na cidade de Pato Branco, Paraná. Como estamos na etapa de aquisição de materiais de construção, a realização de múltiplos orçamentos se tornou indispensável. Para tornar esse processo mais eficiente, criei uma aplicação que se integra ao site Menor Preço - Nota Paraná, permitindo consultar produtos com notas fiscais emitidas no dia atual. Dessa forma, é possível identificar o estabelecimento que comercializou um determinado produto pelo menor preço.
Além de Pato Branco, posso adquirir materiais de construção em cidades próximas, como Francisco Beltrão, Vitorino, Coronel Vivida e Clevelândia. Por isso, configurei a aplicação para buscar informações dessas localidades, ampliando as opções e aumentando as chances de encontrar preços mais competitivos.
Para tornar as respostas mais amigáveis e inteligentes, conectei os dados coletados a um chatbot utilizando o modelo GPT-4o da OpenAI. O sistema foi desenvolvido com uma aplicação backend em Java 23, utilizando o framework Spring Boot com a lib Spring Boot AI, e um frontend construído com Angular 19.
Resultado final
Quero iniciar o post apresentando o resultado final da implementação e como estou utilizando a ferramenta na prática. Abaixo, compartilho alguns exemplos de perguntas e respostas realizadas no chat para ilustrar seu funcionamento.
Repositório GIT
O código-fonte dos projetos está disponível no GitHub e pode ser acessado nos seguintes repositórios:
- 
Backend (Spring Boot): https://github.com/gasparbarancelli/menor-preco-server
 - 
Frontend (Angular): https://github.com/gasparbarancelli/menor-preco-client
 
Direto ao ponto
A ideia deste post é ser o mais objetivo e técnico possível, demostrando o código-fonte e as partes relevantes da implementação, sem dar voltas ou prolongar explicações.
Projeto Backend
Arquivo pom.xml
No arquivo pom.xml, definimos as dependências do Spring Boot e do spring-ai-openai-spring-boot-starter, que possibilitam a conexão com a API da OpenAI:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.4.1</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.gasparbarancelli</groupId>
	<artifactId>menor-preco</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>menor-preco</name>
	<description>Demo project for Spring Boot</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>23</java.version>
		<spring-ai.version>1.0.0-M4</spring-ai.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>
	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.ai</groupId>
				<artifactId>spring-ai-bom</artifactId>
				<version>${spring-ai.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>
	<repositories>
		<repository>
			<id>spring-milestones</id>
			<name>Spring Milestones</name>
			<url>https://repo.spring.io/milestone</url>
			<snapshots>
				<enabled>false</enabled>
			</snapshots>
		</repository>
	</repositories>
</project>
As configurações mais importantes aqui são:
- 
A dependência
spring-ai-openai-spring-boot-starter. - 
O BOM (
spring-ai-bom) para gerenciar versões de dependência. 
Arquivo application.properties
Neste arquivo, configuramos a chave da API (spring.ai.openai.api-key) e definimos o modelo do ChatGPT e as funções que serão usadas:
spring.application.name=menor-preco
spring.ai.openai.api-key=${API_KEY}
spring.ai.openai.chat.options.model=gpt-4o
spring.ai.openai.chat.options.functions=functionSearchCategories,functionSearchProducts
Observação: Substitua ${API_KEY} pela sua chave secreta da OpenAI, ou informe como variável de ambiente.
MenorPrecoApplication.java
Classe principal que inicia a aplicação Spring Boot:
package com.gasparbarancelli;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MenorPrecoApplication {
	public static void main(String[] args) {
		SpringApplication.run(MenorPrecoApplication.class, args);
	}
}
ChatbotApi.java
Cria um endpoint HTTP que recebe a mensagem do usuário (/) e interage com o modelo da OpenAI via Spring AI. A mensagem do usuário é adicionada à lista de mensagens, enviamos a prompt para o ChatGPT e retornamos a resposta em texto puro.
package com.gasparbarancelli.transport.http;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.List;
@RestController
@RequestMapping("/")
public class ChatbotApi {
    private final OpenAiChatModel openAiChatClient;
    private List<Message> messages = new ArrayList<>();
    public ChatbotApi(OpenAiChatModel openAiChatClient) {
        this.openAiChatClient = openAiChatClient;
        messages.add(new SystemMessage("""
                Você é um assistente especialista em compras, dedicado a ajudar os usuários a economizar.
                Sua missão é fornecer sugestões detalhadas e precisas sobre os produtos mais econômicos disponíveis.
                Diretrizes para suas respostas:
                1. Sempre priorize listar os produtos com os menores preços e ofereça detalhes relevantes, como:
                   - Nome do produto
                   - Preço
                   - Estabelecimento
                   - Endereço (se disponível)
                2. Responda sempre no formato Markdown, garantindo que suas mensagens sejam claras e visualmente organizadas.
                3. Utilize os seguintes elementos do Markdown:
                   - Títulos para organizar a informação.
                   - Texto em negrito e itálico para destacar informações importantes.
                   - Listas ordenadas ou não ordenadas para apresentar múltiplas opções de forma legível.
                   - Blocos de código, se necessário, para apresentar dados técnicos.
                   - Links para direcionar o usuário a mais informações ou compras online.
                4. Certifique-se de que o Markdown gerado seja bem estruturado e fácil de entender.
                Sua prioridade é ajudar o usuário a economizar de forma eficiente e fornecer respostas que transmitam confiança e profissionalismo.
                """));
    }
    @GetMapping
    public String hello(@RequestParam("message") String message) {
        UserMessage userMessage = new UserMessage(message);
        messages.add(userMessage);
        ChatResponse response = openAiChatClient.call(new Prompt(messages));
        var content = response.getResult().getOutput().getContent();
        messages.add(new AssistantMessage(content));
        return content;
    }
}
Pontos de destaque:
- 
messagesmantém o histórico do chat, começando com umaSystemMessageque configura o papel do assistente. - 
A cada requisição GET, o parâmetro
messageé adicionado como umaUserMessage. - 
O
openAiChatClient.call()envia esse contexto para a OpenAI e retorna a resposta do modelo. 
ProductService.java
Classe responsável por chamar a API do Menor Preço e buscar informações de categorias e produtos em lote.
package com.gasparbarancelli.interactors;
import com.gasparbarancelli.entities.CategoryResponse;
import com.gasparbarancelli.entities.Location;
import com.gasparbarancelli.entities.ProductResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClient;
import java.util.ArrayList;
import java.util.List;
@Service
public class ProductService {
    private static final String CATEGORY_API_URL = "https://menorpreco.notaparana.pr.gov.br/api/v1/categorias";
    private static final String PRODUCT_API_URL = "https://menorpreco.notaparana.pr.gov.br/api/v1/produtos";
    private static final int BATCH_SIZE = 50;
    private final RestClient restClient;
    public ProductService() {
        this.restClient = RestClient.builder().build();
    }
    public List<CategoryResponse.Category> fetchCategories(String product) {
        try {
            String url = String.format("%s?local=%s&termo=%s&raio=2",
                    CATEGORY_API_URL, Location.PATO_BRANCO.getCode(), product);
            CategoryResponse response = restClient.method(HttpMethod.GET)
                    .uri(url)
                    .header(HttpHeaders.ACCEPT, "application/json")
                    .retrieve()
                    .body(CategoryResponse.class);
            return response.categories();
        } catch (Exception e) {
            throw new RuntimeException("Error fetching categories for product: " + product, e);
        }
    }
    public List<ProductResponse.Product> searchProductsInLocation(String location, String product, String categoryId) {
        List<ProductResponse.Product> allProducts = new ArrayList<>();
        int offset = 0;
        while (true) {
            try {
                String url = String.format("%s?local=%s&termo=%s&categoria=%s&offset=%d&raio=2&data=-1&ordem=0",
                        PRODUCT_API_URL, location, product, categoryId, offset);
                ProductResponse response = restClient.method(HttpMethod.GET)
                        .uri(url)
                        .header(HttpHeaders.ACCEPT, "application/json")
                        .retrieve()
                        .body(ProductResponse.class);
                if (response.products() != null && !response.products().isEmpty()) {
                    allProducts.addAll(response.products());
                }
                if (response.products() == null || response.products().size() < BATCH_SIZE) {
                    break;
                }
                offset += BATCH_SIZE;
            } catch (Exception e) {
                throw new RuntimeException("Error fetching products for category: " + categoryId, e);
            }
        }
        return allProducts;
    }
}
Detalhes:
- 
fetchCategories(…): faz a busca de categorias no Menor Preço, sempre iniciando por Pato Branco (conforme exemplo). - 
searchProductsInLocation(…): pagina os resultados (utilizando offset) até não haver mais dados de produtos. 
FunctionSearchCategories.java
Implementa a primeira função que o ChatGPT chama, para descobrir a categoria de um determinado produto.
package com.gasparbarancelli.interactors;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.gasparbarancelli.entities.CategoryResponse;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.function.Function;
@Service
@Description("Antes de conseguirmos as informações dos materiais de construções precisamos identificar a sua categoria")
public class FunctionSearchCategories implements Function<FunctionSearchCategories.Request, FunctionSearchCategories.Response> {
    private final ProductService productService;
    public FunctionSearchCategories(ProductService productService) {
        this.productService = productService;
    }
    @JsonClassDescription("Informações referentes as categorias de um material de construção, como quantidade, nome e seu identificador")
    public record Request(String product) {}
    public record Response(List<CategoryResponse.Category> categories) {}
    @Override
    public Response apply(Request request) {
        var product = request.product();
        return new Response(productService.fetchCategories(product));
    }
}
O fluxo é:
- 
Dado um
product, chamarproductService.fetchCategories(…). - 
Retornar a lista de categorias encontradas.
 
FunctionSearchProducts.java
Implementa a segunda função que o ChatGPT pode chamar, para buscar produtos dentro de uma categoria e em determinadas cidades. Aqui usamos Java Virtual Threads (criados no Java 21) para fazer consultas paralelas.
package com.gasparbarancelli.interactors;
import com.fasterxml.jackson.annotation.JsonClassDescription;
import com.gasparbarancelli.entities.Location;
import com.gasparbarancelli.entities.ProductResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Description;
import org.springframework.stereotype.Service;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Stream;
@Service
@Description("Recupera as notas fiscais de produtos comercializados no dia atual, podendo identificar qual foi o preco mais alto ou baixo pago por um produto")
public class FunctionSearchProducts implements Function<FunctionSearchProducts.Request, FunctionSearchProducts.Response> {
    private static final Logger LOGGER = LoggerFactory.getLogger(FunctionSearchProducts.class);
    private final ProductService productService;
    public FunctionSearchProducts(ProductService productService) {
        this.productService = productService;
    }
    @JsonClassDescription("Informações referentes ao material de construção, como descrição, preço e estabelecimento em qual o material foi comercializado")
    public record Request(String product, String categoryId) {}
    public record Response(List<ProductResponse.Product> products) {}
    @Override
    public Response apply(Request request) {
        var product = request.product();
        var categoryId = request.categoryId();
        List<String> locationCodes = Location.getCodes();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            List<Future<List<ProductResponse.Product>>> futures = locationCodes.stream()
                    .map(location -> executor.submit(() -> productService.searchProductsInLocation(location, product, categoryId)))
                    .toList();
            return new Response(futures.stream()
                    .flatMap(future -> {
                        try {
                            return future.get(30, TimeUnit.SECONDS).stream();
                        } catch (Exception e) {
                            LOGGER.info("Erro ou timeout para a localização: " + e.getMessage());
                            return Stream.empty();
                        }
                    })
                    .sorted(Comparator.comparing(ProductResponse.Product::price))
                    .toList());
        }
    }
}
Fluxo:
- 
Recebe
productecategoryIdcomo parâmetros. - 
Recupera os códigos das cidades (
Location.getCodes()). - 
Para cada código de cidade, chama
productService.searchProductsInLocation(…)em paralelo. - 
Ordena todos os produtos obtidos pelo menor preço e retorna ao usuário.
 
Entidades
ProductResponse.java
package com.gasparbarancelli.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import java.util.List;
public record ProductResponse(
    @JsonProperty("produtos") List<Product> products,
    @JsonProperty("total") int total,
    @JsonProperty("precos") ProductPrice price
) {
    public record ProductPrice(
            String min,
            String max
    ) {}
    public record Product(
        @JsonProperty("desc") String description,
        @JsonProperty("valor") String price,
        @JsonProperty("valor_desconto") String discount,
        @JsonProperty("estabelecimento") Company company
    ) {
        public record Company(
            @JsonPropertyDescription("Nome do estabelecimento em que o produto foi vendido")
            @JsonProperty("nm_fan") String name,
            @JsonProperty("tp_logr") String addressType,
            @JsonProperty("nm_logr") String addressName,
            @JsonPropertyDescription("Cidade em que o produto foi comercializado")
            @JsonProperty("mun") String city,
            String uf
        ) {}
    }
}
Guarda a estrutura JSON que recebemos do Menor Preço: lista de produtos, quantidade total e uma faixa de preço (mínimo e máximo).
Location.java
package com.gasparbarancelli.entities;
import java.util.Arrays;
import java.util.List;
public enum Location {
    PATO_BRANCO("6g6dc3jcw"),
    CLEVELANDIA("6g6cb9qch"),
    VITORINO("6g66wfwkw"),
    CORONEL_VIVIDA("6g6s5y7j2"),
    FRANCISCO_BELTRAO("6g678srwp");
    private String code;
    Location(String code) {
        this.code = code;
    }
    public String getCode() {
        return code;
    }
    public static List<String> getCodes() {
        return Arrays.stream(Location.values()).map(Location::getCode).toList();
    }
}
Enum que mapeia as cidades para seus respectivos códigos na API do Menor Preço.
CategoryResponse.java
package com.gasparbarancelli.entities;
import com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
public record CategoryResponse(
    @JsonProperty("categorias") List<Category> categories
) {
    public record Category(
        String id,
        @JsonProperty("qtd") Integer qtd,
        @JsonProperty("desc") String name
    ) {}
}
Modelo para representar as categorias retornadas pela API.
Configuração de CORS
Para permitir que o frontend Angular acesse nossa API em outro endereço (porta 8080 vs 4200), precisamos liberar o CORS:
package com.gasparbarancelli.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")
                        .allowedOrigins("http://localhost:4200")
                        .allowedMethods("GET", "OPTIONS")
                        .allowedHeaders("*")
                        .allowCredentials(true);
            }
        };
    }
}
Projeto Frontend
chat.component.ts
Aqui, enviamos o texto digitado para o ChatService e, em seguida, adicionamos a mensagem de resposta do servidor no array messages.
import { Component } from '@angular/core';
import { ChatService } from './chat.service';
export interface ChatMessage {
  message: string;
  send: boolean;
}
@Component({
  selector: 'app-chat',
  standalone: false,
  templateUrl: './chat.component.html',
  styleUrls: ['./chat.component.scss']
})
export class ChatComponent {
  messages: ChatMessage[] = [];
  inputMessage: string = '';
  constructor(private chatService: ChatService) {}
  sendMessage(): void {
    if (this.inputMessage.trim()) {
      const userMessage: ChatMessage = { message: this.inputMessage, send: true };
      this.messages.push(userMessage);
      this.chatService.send(this.inputMessage).subscribe({
        next: response => {
          const botMessage: ChatMessage = { message: response, send: false };
          this.messages.push(botMessage);
        },
        error: error => {
          console.error('Erro ao enviar mensagem:', error);
      }});
      this.inputMessage = '';
    }
  }
}
chat.component.html
Percorremos o array de mensagens para exibir as mensagens do chat enviadas pelo usuário e pelo chatbot, bem como adicionar um input onde o usuário entra com a mensagens e um botão para enviar a mensagem.
<div class="chat-container">
  <div class="chat-messages">
    <div *ngFor="let msg of messages"
      [ngClass]="{ 'message-send': msg.send, 'message-receive': !msg.send }"
      class="message">
      <markdown
        [data]="msg.message"
        [disableSanitizer]="true">
      </markdown>
    </div>
  </div>
  <div class="chat-input">
    <input
      type="text"
      [(ngModel)]="inputMessage"
      placeholder="Digite uma mensagem"
      (keydown.enter)="sendMessage()"/>
    <button (click)="sendMessage()">Enviar</button>
  </div>
</div>
chat.component.scss
Definimos um estilo para o nosso componente de Chat.
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100%;
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 10px;
}
.chat-messages {
  flex: 1;
  overflow-y: auto;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.message {
  padding: 10px;
  border-radius: 8px;
  max-width: 60%;
  word-wrap: break-word;
}
.message-send {
  align-self: flex-end;
  background-color: #007bff;
  color: white;
  text-align: left;
}
.message-receive {
  align-self: flex-start;
  background-color: #f1f0f0;
  color: black;
  text-align: left;
}
.chat-input {
  display: flex;
  gap: 5px;
}
.chat-input input {
  flex: 1;
  padding: 10px;
  border: 1px solid #ccc;
  border-radius: 8px;
}
.chat-input button {
  padding: 10px;
  background-color: #007bff;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}
.chat-input button:hover {
  background-color: #0056b3;
}
chat.service.ts
Serviço que se comunica com nosso backend (localhost:8080) para enviar a mensagem.
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import {Observable} from 'rxjs';
@Injectable()
export class ChatService {
  constructor(private httpClient: HttpClient) {
  }
  send(message: string): Observable<string> {
    const url = `http://localhost:8080/?message=${message}`;
    return this.httpClient.get(url, {responseType: 'text'});
  }
}
chat.module.ts
Este módulo registra o ChatComponent, ChatService e o suporte a Markdown.
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ChatComponent} from './chat.component';
import {ChatService} from './chat.service';
import {FormsModule} from '@angular/forms';
import {MarkdownComponent, provideMarkdown} from 'ngx-markdown';
@NgModule({
  declarations: [
    ChatComponent
  ],
  exports: [
    ChatComponent
  ],
  imports: [
    CommonModule,
    FormsModule,
    MarkdownComponent
  ],
  providers: [
    ChatService,
    provideMarkdown()
  ]
})
export class ChatModule {
}
Instalação de pacote
O único componente necessário para instalação é aquele responsável por renderizar Markdown. Para isso, execute o seguinte comando:
npm install ngx-markdown marked@^15.0.0 --save
app.component.html
Adicionamos apenas o componente de chat.
<app-chat></app-chat>
app.component.ts
Seguimos com a implementação padrão.
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  standalone: false,
})
export class AppComponent {
}
app.module.ts
Declaramos o modulo do componente de Chat bem como do HttpClientModule, para que possamos realizar chamadas http para o nosso backend.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import {ChatModule} from './chat/chat.module';
import {HttpClientModule} from '@angular/common/http';
@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    ChatModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
Por fim, basta rodar o ng serve no frontend e mvn spring-boot:run no backend.
Conclusão
A ideia central deste projeto foi demonstrar o poder que temos ao integrar uma fonte de dados ao chatbot, no meu caso agora eu tenho maior chance de economizar na compra de materiais de construção, integrando o ChatGPT (via funções específicas) com a API do Menor Preço do Nota Paraná.
De forma que:
- 
Usuário pergunta: “Qual o menor preço para cimento hoje?”
 - 
ChatGPT recebe a pergunta e chama internamente a função
functionSearchCategoriespara descobrir a categoria de “cimento”. - 
Em seguida, chama a função
functionSearchProductsinformando ocategoryIdretornado. - 
O sistema retorna, em formato
Markdown, a lista de estabelecimentos e preços (do menor para o maior) para o produto “cimento” nas cidades de interesse (Pato Branco, Clevelandia, Vitorino, Coronel Vivida e Francisco Beltrão). 
Com isso, posso entrar em contato com o estabelecimento que está oferecendo o melhor custo e efetuar a compra!