Pular para o conteúdo principal

Consultando o histórico de custo dos produtos pela API

O endpoint de histórico de custo permite consultar a evolução da quantidade, do custo médio e do valor total dos produtos. Os dados podem ser usados em relatórios, integrações e ferramentas de análise, como o Power BI.

Antes de começar

Esta consulta exige autenticação. Consulte o artigo API FoxManager: Guia Completo de Integração com Swagger para aprender a obter o token e autorizar o acesso.

Endpoint

Use uma requisição GET no endereço:

https://api.foxmanager.com.br/api/fox/product_cost_history/

O endpoint é somente para consulta e não altera informações no FoxManager.

Empresa consultada

A empresa é obrigatória e definida automaticamente pelo token utilizado na autenticação. Não é necessário informar a empresa nos parâmetros da URL.

Cada token permite consultar somente os dados da empresa à qual ele está associado. Para consultar outra empresa, use um token gerado para ela.

Segurança do token

Não coloque o token em documentos públicos, código-fonte, planilhas compartilhadas ou capturas de tela. Guarde-o em um local seguro e restrinja o acesso ao relatório do Power BI.

Parâmetros da consulta

ParâmetroObrigatórioDescriçãoExemplo
start_dateSimData inicial das movimentações no formato AAAA-MM-DD.2026-01-01
finish_dateSimData final das movimentações no formato AAAA-MM-DD.2026-01-31
productNãoIdentificador do produto no FoxManager.12345
nfeNãoIdentificador interno da NFe no FoxManager.67890
orderingNãoCampo usado para ordenar o resultado. O padrão é -id.movement_date,id
limitNãoQuantidade por página. O padrão é 500 e o máximo é 1000.1000
offsetNãoPosição inicial da página. O padrão é zero.0

Consulta básica

Informe o período inicial e final:

GET https://api.foxmanager.com.br/api/fox/product_cost_history/?start_date=2026-01-01&finish_date=2026-01-31

Filtrar por produto

O campo product recebe o identificador numérico do produto:

GET https://api.foxmanager.com.br/api/fox/product_cost_history/?start_date=2026-01-01&finish_date=2026-01-31&product=12345

Filtrar por NFe

O campo nfe recebe o identificador interno da nota fiscal, e não o número impresso da nota:

GET https://api.foxmanager.com.br/api/fox/product_cost_history/?start_date=2026-01-01&finish_date=2026-01-31&nfe=67890

O identificador pode ser obtido pela URI retornada no endpoint de NFe:

https://api.foxmanager.com.br/api/fox/nfe/67890/

Ordenação

Por padrão, a API retorna os registros com o maior identificador primeiro.

Para ordenar do registro mais antigo para o mais recente, use:

&ordering=movement_date,id

Para ordenar do mais recente para o mais antigo, use:

&ordering=-movement_date,-id

O campo id deve ser mantido como critério adicional quando houver mais de uma movimentação na mesma data.

Estrutura da resposta

{
"count": 2,
"next": null,
"previous": null,
"results": [
{
"id": 10002,
"url": "https://api.foxmanager.com.br/api/fox/product_cost_history/10002/",
"movement_date": "2026-01-15",
"quantity": 10,
"average_cost": 125.50,
"total_amount": 1255.00,
"serial_number": "ABC123456",
"company": "https://api.foxmanager.com.br/api/fox/company/100/",
"product": "https://api.foxmanager.com.br/api/fox/product/12345/",
"nfe": "https://api.foxmanager.com.br/api/fox/nfe/67890/",
"nfe_item": "https://api.foxmanager.com.br/api/fox/nfe_item/98765/"
},
{
"id": 10001,
"url": "https://api.foxmanager.com.br/api/fox/product_cost_history/10001/",
"movement_date": "2026-01-01",
"quantity": 5,
"average_cost": 120.00,
"total_amount": 600.00,
"serial_number": null,
"company": "https://api.foxmanager.com.br/api/fox/company/100/",
"product": "https://api.foxmanager.com.br/api/fox/product/12345/",
"nfe": null,
"nfe_item": null
}
]
}

Campos retornados

CampoTipoDescrição
countInteiroTotal de registros encontrados considerando os filtros informados.
nextURI ou nuloEndereço da próxima página. Será null quando não houver outra página.
previousURI ou nuloEndereço da página anterior.
resultsListaRegistros retornados na página atual.
idInteiroIdentificador do histórico de custo. Também define a sequência quando existem movimentações na mesma data.
urlURIEndereço do registro do histórico de custo.
movement_dateDataData considerada no histórico de custo, no formato AAAA-MM-DD.
quantityDecimalSaldo acumulado em quantidade após a movimentação. Não representa isoladamente a quantidade movimentada na nota.
average_costDecimalCusto médio unitário do produto após a movimentação.
total_amountDecimalValor total do saldo após a movimentação.
serial_numberTexto ou nuloNúmero de série do produto relacionado ao item da NFe. Será null quando o item não possuir número de série.
companyURIEndereço do cadastro da empresa relacionada ao histórico.
productURIEndereço do cadastro do produto relacionado ao histórico.
nfeURI ou nuloEndereço da NFe relacionada. Será null para uma carga inicial sem documento fiscal.
nfe_itemURI ou nuloEndereço do item da NFe que originou a movimentação de custo. Será null para uma carga inicial.
Carga inicial

Quando nfe for null, o registro normalmente representa a carga inicial do histórico de custo. Nesse caso, nfe_item e serial_number também serão null. Os registros seguintes demonstram a evolução do saldo e do custo do produto.

Produtos controlados por número de série

Quando uma NFe movimenta mais de uma unidade de um produto controlado por número de série, a API pode retornar um registro para cada item processado. Cada registro apresenta:

  • a mesma URI em nfe;
  • uma URI própria em nfe_item;
  • o número correspondente em serial_number;
  • o saldo acumulado do produto após aquela movimentação.

Exemplo simplificado de uma NFe com duas unidades serializadas:

[
{
"quantity": 11,
"average_cost": 500.00,
"total_amount": 5500.00,
"serial_number": "SERIE-001",
"nfe": "https://api.foxmanager.com.br/api/fox/nfe/67890/",
"nfe_item": "https://api.foxmanager.com.br/api/fox/nfe_item/98765/"
},
{
"quantity": 10,
"average_cost": 500.00,
"total_amount": 5000.00,
"serial_number": "SERIE-002",
"nfe": "https://api.foxmanager.com.br/api/fox/nfe/67890/",
"nfe_item": "https://api.foxmanager.com.br/api/fox/nfe_item/98766/"
}
]
Saldo após a movimentação

O campo quantity representa o saldo acumulado após cada movimentação. Ele não informa a quantidade vendida em cada item. Por isso, uma mesma NFe pode aparecer em registros sucessivos com quantidades diferentes.

Os campos nfe_item e serial_number fazem parte do retorno. Eles não são filtros da consulta. Para localizar os registros de uma nota, continue utilizando o parâmetro nfe.

Dúvidas sobre a interpretação dos dados

Por que uma mesma NFe pode apresentar itens com custo e outros com custo zero?

O custo é apurado separadamente para cada produto. Por isso, os itens de uma mesma NFe podem apresentar resultados diferentes.

Um produto pode aparecer com average_cost e total_amount iguais a zero quando não houver um custo anterior disponível para a movimentação, como uma entrada com valor ou uma carga inicial de custo. Isso não significa que toda a NFe esteja sem custo.

Para analisar esse caso, utilize product, nfe_item e serial_number para identificar exatamente qual produto e item originaram o registro.

Por que o mesmo produto pode aparecer várias vezes na mesma NFe?

Cada registro representa uma movimentação de custo vinculada a um item da NFe. Quando o mesmo produto está cadastrado em vários itens, especialmente em produtos controlados por número de série, a API retorna esses itens separadamente.

Nessa situação:

  • nfe permanece igual porque os registros pertencem à mesma nota;
  • product pode permanecer igual porque se trata do mesmo produto;
  • nfe_item identifica cada item processado;
  • serial_number identifica a unidade, quando o produto possui controle por série.

Portanto, várias linhas para o mesmo produto e a mesma NFe não significam necessariamente que a nota foi processada mais de uma vez.

Por que quantity aparece em sequência?

O campo quantity apresenta o saldo acumulado do produto depois de cada movimentação. Ele não representa a quantidade vendida naquele item da nota.

Se uma NFe movimentar várias unidades separadamente, os registros poderão mostrar saldos sucessivos, como 113, 112, 111 e 110. Essa sequência demonstra a atualização do saldo a cada item processado.

Por que average_cost permanece igual e total_amount muda?

Nas saídas, o custo médio pode permanecer igual enquanto o saldo é reduzido. O campo total_amount acompanha essa alteração porque representa o valor total do saldo após a movimentação:

total_amount = quantity × average_cost

Exemplo:

ItemSaldo após a movimentaçãoCusto médioValor total do saldo
Unidade com série SERIE-00111500,005.500,00
Unidade com série SERIE-00210500,005.000,00

Os dois registros podem pertencer à mesma NFe e ao mesmo produto, mas terão nfe_item e, quando aplicável, serial_number diferentes.

Como percorrer todas as páginas

A API usa paginação com limit e offset. Para obter todos os registros:

  1. Faça a primeira consulta com offset=0.
  2. Leia os registros dentro de results.
  3. Verifique o campo next.
  4. Se next possuir uma URI, faça uma nova consulta usando exatamente esse endereço.
  5. Repita o processo até next retornar null.
Paginação

Não use apenas a primeira página para montar o relatório. O campo count informa o total encontrado, enquanto results contém somente os registros da página atual.

Exemplo no Power BI

No Editor do Power Query, crie uma consulta em branco e utilize um código semelhante ao exemplo abaixo. Substitua SEU_TOKEN pelo token fornecido para a integração.

let
BaseUrl = "https://api.foxmanager.com.br",
Caminho = "api/fox/product_cost_history/",
Token = "Token SEU_TOKEN",
DataInicial = "2026-01-01",
DataFinal = "2026-01-31",
Limite = 1000,

ObterPagina = (Offset as number) as record =>
Json.Document(
Web.Contents(
BaseUrl,
[
RelativePath = Caminho,
Query = [
start_date = DataInicial,
finish_date = DataFinal,
limit = Text.From(Limite),
offset = Text.From(Offset)
],
Headers = [Authorization = Token]
]
)
),

PrimeiraPagina = ObterPagina(0),
TotalPaginas = Number.RoundUp(
PrimeiraPagina[count] / Limite
),

OffsetsAdicionais =
if TotalPaginas > 1
then List.Transform({1..(TotalPaginas - 1)}, each _ * Limite)
else {},

Paginas =
{PrimeiraPagina[results]} &
List.Transform(
OffsetsAdicionais,
each ObterPagina(_)[results]
),

Registros = List.Combine(Paginas),
Tabela = Table.FromRecords(Registros)
in
Tabela
Proteja o token

O exemplo utiliza SEU_TOKEN apenas como espaço reservado. Em uma integração definitiva, armazene a credencial de forma segura e controle quem pode editar ou baixar o arquivo do Power BI.

Atualização por período

Para reduzir o volume de dados, configure o Power BI para consultar somente o período necessário. Os campos start_date e finish_date são obrigatórios e podem ser vinculados aos parâmetros de atualização da consulta.

Ao recarregar um período já consultado, use o campo id como chave do histórico para evitar registros duplicados no modelo do Power BI.

Respostas de erro mais comuns

CódigoSignificadoComo corrigir
400Período ausente ou parâmetro inválido.Informe start_date e finish_date no formato AAAA-MM-DD.
401Token ausente ou inválido.Confira o cabeçalho Authorization e utilize Token antes da chave.
403Usuário sem permissão para consultar NFe e histórico de custo.Solicite a revisão das permissões do usuário.
404Registro ou endereço não encontrado.Confira a URI e os identificadores informados.
500Erro inesperado no processamento.Se persistir, entre em contato com o suporte do FoxManager.

Boas práticas

  • Informe sempre um período compatível com o relatório.
  • Utilize limit=1000 em cargas controladas para reduzir a quantidade de requisições.
  • Percorra todas as páginas usando o campo next.
  • Use product ou nfe quando precisar investigar registros específicos.
  • Mantenha movement_date,id na ordenação cronológica.
  • Trate company, product, nfe e nfe_item como URIs relacionadas a outros endpoints.
  • Considere serial_number como opcional e aceite null para produtos sem controle por série.
  • Não exponha o token em relatórios, logs ou arquivos compartilhados.
AjudaNormalmente responde dentro de um dia
Ajuda

Olá! 👋 O que podemos fazer por você?

04:58