Add Tiny ERP order metadata ingestion
This commit is contained in:
@@ -27,6 +27,12 @@ const initDB = async () => {
|
|||||||
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS pedido_id VARCHAR(100);`).catch(() => {});
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS pedido_id VARCHAR(100);`).catch(() => {});
|
||||||
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_fone VARCHAR(50);`).catch(() => {});
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_fone VARCHAR(50);`).catch(() => {});
|
||||||
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS data_pedido_date DATE;`).catch(() => {});
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS data_pedido_date DATE;`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS cliente_nome_fantasia VARCHAR(255);`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS id_vendedor VARCHAR(100);`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS nome_vendedor VARCHAR(255);`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS marketplace VARCHAR(255);`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS canal_venda VARCHAR(255);`).catch(() => {});
|
||||||
|
await pool.query(`ALTER TABLE orders ADD COLUMN IF NOT EXISTS numero_ecommerce VARCHAR(100);`).catch(() => {});
|
||||||
|
|
||||||
await pool.query(`
|
await pool.query(`
|
||||||
ALTER TABLE orders
|
ALTER TABLE orders
|
||||||
|
|||||||
@@ -8,7 +8,13 @@ const formatOrderRow = (row) => ({
|
|||||||
Valor_Unitario: parseFloat(row.valor_unitario),
|
Valor_Unitario: parseFloat(row.valor_unitario),
|
||||||
Recebido_Em: row.created_at,
|
Recebido_Em: row.created_at,
|
||||||
ID_Pedido: row.pedido_id,
|
ID_Pedido: row.pedido_id,
|
||||||
Fone_Cliente: row.cliente_fone
|
Fone_Cliente: row.cliente_fone,
|
||||||
|
cliente_nome_fantasia: row.cliente_nome_fantasia || '',
|
||||||
|
id_vendedor: row.id_vendedor || '',
|
||||||
|
nome_vendedor: row.nome_vendedor || '',
|
||||||
|
marketplace: row.marketplace || '',
|
||||||
|
canal_venda: row.canal_venda || '',
|
||||||
|
numero_ecommerce: row.numero_ecommerce || ''
|
||||||
});
|
});
|
||||||
|
|
||||||
const normalizeOrderDate = (dateValue) => {
|
const normalizeOrderDate = (dateValue) => {
|
||||||
@@ -35,6 +41,17 @@ const normalizeOrderDate = (dateValue) => {
|
|||||||
return date.toISOString().slice(0, 10);
|
return date.toISOString().slice(0, 10);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const pickFirstValue = (item, fieldNames) => {
|
||||||
|
for (const fieldName of fieldNames) {
|
||||||
|
const value = item[fieldName];
|
||||||
|
if (value !== undefined && value !== null && value !== '') {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return '';
|
||||||
|
};
|
||||||
|
|
||||||
const normalizeOrderPayload = (item) => {
|
const normalizeOrderPayload = (item) => {
|
||||||
const fallbackId = `${item.Nome_Cliente}_${item.Data_Pedido}_${item.Valor_Pedido}`;
|
const fallbackId = `${item.Nome_Cliente}_${item.Data_Pedido}_${item.Valor_Pedido}`;
|
||||||
const orderId = item.id || item.ID_Pedido || (item.json && item.json.body && item.json.body.id) || fallbackId;
|
const orderId = item.id || item.ID_Pedido || (item.json && item.json.body && item.json.body.id) || fallbackId;
|
||||||
@@ -51,7 +68,13 @@ const normalizeOrderPayload = (item) => {
|
|||||||
parseInt(item.Quantidade, 10) || 0,
|
parseInt(item.Quantidade, 10) || 0,
|
||||||
parseFloat(item.Valor_Unitario) || 0,
|
parseFloat(item.Valor_Unitario) || 0,
|
||||||
String(orderId),
|
String(orderId),
|
||||||
String(fone)
|
String(fone),
|
||||||
|
String(pickFirstValue(item, ['nome_fantasia', 'Nome_Fantasia', 'Cliente_Nome_Fantasia', 'cliente_nome_fantasia'])),
|
||||||
|
String(pickFirstValue(item, ['id_vendedor', 'ID_Vendedor'])),
|
||||||
|
String(pickFirstValue(item, ['nome_vendedor', 'Nome_Vendedor'])),
|
||||||
|
String(pickFirstValue(item, ['marketplace', 'Marketplace', 'nome_ecommerce', 'Nome_Ecommerce'])),
|
||||||
|
String(pickFirstValue(item, ['canal_venda', 'Canal_Venda'])),
|
||||||
|
String(pickFirstValue(item, ['numero_ecommerce', 'Numero_Ecommerce']))
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -507,7 +507,13 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
|||||||
produto_descricao,
|
produto_descricao,
|
||||||
quantidade,
|
quantidade,
|
||||||
valor_unitario,
|
valor_unitario,
|
||||||
pedido_id
|
pedido_id,
|
||||||
|
cliente_nome_fantasia,
|
||||||
|
id_vendedor,
|
||||||
|
nome_vendedor,
|
||||||
|
marketplace,
|
||||||
|
canal_venda,
|
||||||
|
numero_ecommerce
|
||||||
FROM orders
|
FROM orders
|
||||||
WHERE ${periodFilters.join(' AND ')}
|
WHERE ${periodFilters.join(' AND ')}
|
||||||
ORDER BY data_pedido_date DESC, COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text) DESC;
|
ORDER BY data_pedido_date DESC, COALESCE(NULLIF(pedido_id, ''), data_pedido || '_' || valor_pedido::text) DESC;
|
||||||
@@ -559,7 +565,13 @@ const getClientDetailsAnalytics = async (clientToken, range = {}) => {
|
|||||||
Quantidade: toNumber(row.quantidade),
|
Quantidade: toNumber(row.quantidade),
|
||||||
Valor_Unitario: toNumber(row.valor_unitario),
|
Valor_Unitario: toNumber(row.valor_unitario),
|
||||||
ID_Pedido: row.pedido_id || '',
|
ID_Pedido: row.pedido_id || '',
|
||||||
Fone_Cliente: row.cliente_fone || ''
|
Fone_Cliente: row.cliente_fone || '',
|
||||||
|
cliente_nome_fantasia: row.cliente_nome_fantasia || '',
|
||||||
|
id_vendedor: row.id_vendedor || '',
|
||||||
|
nome_vendedor: row.nome_vendedor || '',
|
||||||
|
marketplace: row.marketplace || '',
|
||||||
|
canal_venda: row.canal_venda || '',
|
||||||
|
numero_ecommerce: row.numero_ecommerce || ''
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ const upsertOrders = async (payload) => {
|
|||||||
const insertQuery = `
|
const insertQuery = `
|
||||||
INSERT INTO orders (
|
INSERT INTO orders (
|
||||||
cliente_nome, data_pedido, data_pedido_date, valor_pedido,
|
cliente_nome, data_pedido, data_pedido_date, valor_pedido,
|
||||||
produto_id, produto_descricao, quantidade, valor_unitario, pedido_id, cliente_fone
|
produto_id, produto_descricao, quantidade, valor_unitario, pedido_id, cliente_fone,
|
||||||
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
|
cliente_nome_fantasia, id_vendedor, nome_vendedor, marketplace, canal_venda, numero_ecommerce
|
||||||
|
) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
||||||
ON CONFLICT (pedido_id, produto_id) DO UPDATE SET
|
ON CONFLICT (pedido_id, produto_id) DO UPDATE SET
|
||||||
cliente_nome = EXCLUDED.cliente_nome,
|
cliente_nome = EXCLUDED.cliente_nome,
|
||||||
data_pedido = EXCLUDED.data_pedido,
|
data_pedido = EXCLUDED.data_pedido,
|
||||||
@@ -26,6 +27,12 @@ const upsertOrders = async (payload) => {
|
|||||||
quantidade = EXCLUDED.quantidade,
|
quantidade = EXCLUDED.quantidade,
|
||||||
valor_unitario = EXCLUDED.valor_unitario,
|
valor_unitario = EXCLUDED.valor_unitario,
|
||||||
cliente_fone = EXCLUDED.cliente_fone,
|
cliente_fone = EXCLUDED.cliente_fone,
|
||||||
|
cliente_nome_fantasia = EXCLUDED.cliente_nome_fantasia,
|
||||||
|
id_vendedor = EXCLUDED.id_vendedor,
|
||||||
|
nome_vendedor = EXCLUDED.nome_vendedor,
|
||||||
|
marketplace = EXCLUDED.marketplace,
|
||||||
|
canal_venda = EXCLUDED.canal_venda,
|
||||||
|
numero_ecommerce = EXCLUDED.numero_ecommerce,
|
||||||
created_at = CURRENT_TIMESTAMP
|
created_at = CURRENT_TIMESTAMP
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const assert = require('node:assert/strict');
|
const assert = require('node:assert/strict');
|
||||||
const test = require('node:test');
|
const test = require('node:test');
|
||||||
|
|
||||||
const { normalizeOrderDate, normalizeOrderPayload } = require('../mappers/orderMapper');
|
const { formatOrderRow, normalizeOrderDate, normalizeOrderPayload } = require('../mappers/orderMapper');
|
||||||
|
|
||||||
test('normalizeOrderDate accepts Brazilian display dates', () => {
|
test('normalizeOrderDate accepts Brazilian display dates', () => {
|
||||||
assert.equal(normalizeOrderDate('28/05/2026'), '2026-05-28');
|
assert.equal(normalizeOrderDate('28/05/2026'), '2026-05-28');
|
||||||
@@ -35,3 +35,83 @@ test('normalizeOrderPayload includes normalized date without changing display da
|
|||||||
assert.equal(payload[1], '28/05/2026');
|
assert.equal(payload[1], '28/05/2026');
|
||||||
assert.equal(payload[2], '2026-05-28');
|
assert.equal(payload[2], '2026-05-28');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('normalizeOrderPayload maps Tiny ERP metadata aliases', () => {
|
||||||
|
const payload = normalizeOrderPayload({
|
||||||
|
Nome_Cliente: 'Cliente Teste',
|
||||||
|
Data_Pedido: '28/05/2026',
|
||||||
|
Valor_Pedido: '120.50',
|
||||||
|
ID_Produto: 'SKU-1',
|
||||||
|
Descricao_Produto: 'Produto',
|
||||||
|
Quantidade: '2',
|
||||||
|
Valor_Unitario: '60.25',
|
||||||
|
ID_Pedido: 'ORDER-1',
|
||||||
|
Nome_Fantasia: 'Cliente Fantasia',
|
||||||
|
ID_Vendedor: 123,
|
||||||
|
Nome_Vendedor: 'Maria',
|
||||||
|
Nome_Ecommerce: 'Mercado Livre',
|
||||||
|
Canal_Venda: 'Online',
|
||||||
|
Numero_Ecommerce: 'EC-987'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(payload[10], 'Cliente Fantasia');
|
||||||
|
assert.equal(payload[11], '123');
|
||||||
|
assert.equal(payload[12], 'Maria');
|
||||||
|
assert.equal(payload[13], 'Mercado Livre');
|
||||||
|
assert.equal(payload[14], 'Online');
|
||||||
|
assert.equal(payload[15], 'EC-987');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('normalizeOrderPayload maps lower-case metadata names', () => {
|
||||||
|
const payload = normalizeOrderPayload({
|
||||||
|
Nome_Cliente: 'Cliente Teste',
|
||||||
|
Data_Pedido: '28/05/2026',
|
||||||
|
Valor_Pedido: '120.50',
|
||||||
|
ID_Produto: 'SKU-1',
|
||||||
|
Descricao_Produto: 'Produto',
|
||||||
|
Quantidade: '2',
|
||||||
|
Valor_Unitario: '60.25',
|
||||||
|
ID_Pedido: 'ORDER-1',
|
||||||
|
cliente_nome_fantasia: 'Fantasia Lower',
|
||||||
|
id_vendedor: 'VEN-7',
|
||||||
|
nome_vendedor: 'Joao',
|
||||||
|
marketplace: 'Shopee',
|
||||||
|
canal_venda: 'Marketplace',
|
||||||
|
numero_ecommerce: '100200'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(payload[10], 'Fantasia Lower');
|
||||||
|
assert.equal(payload[11], 'VEN-7');
|
||||||
|
assert.equal(payload[12], 'Joao');
|
||||||
|
assert.equal(payload[13], 'Shopee');
|
||||||
|
assert.equal(payload[14], 'Marketplace');
|
||||||
|
assert.equal(payload[15], '100200');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('formatOrderRow returns Tiny ERP metadata fields', () => {
|
||||||
|
const row = formatOrderRow({
|
||||||
|
cliente_nome: 'Cliente Teste',
|
||||||
|
data_pedido: '28/05/2026',
|
||||||
|
valor_pedido: '120.50',
|
||||||
|
produto_id: 'SKU-1',
|
||||||
|
produto_descricao: 'Produto',
|
||||||
|
quantidade: 2,
|
||||||
|
valor_unitario: '60.25',
|
||||||
|
created_at: '2026-05-28T12:00:00.000Z',
|
||||||
|
pedido_id: 'ORDER-1',
|
||||||
|
cliente_fone: '(16) 99999-9999',
|
||||||
|
cliente_nome_fantasia: 'Cliente Fantasia',
|
||||||
|
id_vendedor: '123',
|
||||||
|
nome_vendedor: 'Maria',
|
||||||
|
marketplace: 'Mercado Livre',
|
||||||
|
canal_venda: 'Online',
|
||||||
|
numero_ecommerce: 'EC-987'
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(row.cliente_nome_fantasia, 'Cliente Fantasia');
|
||||||
|
assert.equal(row.id_vendedor, '123');
|
||||||
|
assert.equal(row.nome_vendedor, 'Maria');
|
||||||
|
assert.equal(row.marketplace, 'Mercado Livre');
|
||||||
|
assert.equal(row.canal_venda, 'Online');
|
||||||
|
assert.equal(row.numero_ecommerce, 'EC-987');
|
||||||
|
});
|
||||||
|
|||||||
83
backend/test/ordersService.test.js
Normal file
83
backend/test/ordersService.test.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
const assert = require('node:assert/strict');
|
||||||
|
const test = require('node:test');
|
||||||
|
|
||||||
|
test('upsertOrders persists Tiny ERP metadata columns', async () => {
|
||||||
|
const queries = [];
|
||||||
|
const client = {
|
||||||
|
query: async (sql, params) => {
|
||||||
|
queries.push({ sql, params });
|
||||||
|
return { rows: [] };
|
||||||
|
},
|
||||||
|
release: () => {
|
||||||
|
queries.push({ sql: 'RELEASE' });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const dbPath = require.resolve('../db');
|
||||||
|
const servicePath = require.resolve('../services/ordersService');
|
||||||
|
const originalDbCache = require.cache[dbPath];
|
||||||
|
const originalServiceCache = require.cache[servicePath];
|
||||||
|
|
||||||
|
delete require.cache[servicePath];
|
||||||
|
require.cache[dbPath] = {
|
||||||
|
id: dbPath,
|
||||||
|
filename: dbPath,
|
||||||
|
loaded: true,
|
||||||
|
exports: {
|
||||||
|
pool: {
|
||||||
|
connect: async () => client
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { upsertOrders } = require('../services/ordersService');
|
||||||
|
|
||||||
|
await upsertOrders([{
|
||||||
|
Nome_Cliente: 'Cliente Teste',
|
||||||
|
Data_Pedido: '28/05/2026',
|
||||||
|
Valor_Pedido: '120.50',
|
||||||
|
ID_Produto: 'SKU-1',
|
||||||
|
Descricao_Produto: 'Produto',
|
||||||
|
Quantidade: '2',
|
||||||
|
Valor_Unitario: '60.25',
|
||||||
|
ID_Pedido: 'ORDER-1',
|
||||||
|
Fone_Cliente: '(16) 99999-9999',
|
||||||
|
cliente_nome_fantasia: 'Cliente Fantasia',
|
||||||
|
id_vendedor: 'VEN-1',
|
||||||
|
nome_vendedor: 'Maria',
|
||||||
|
marketplace: 'Mercado Livre',
|
||||||
|
canal_venda: 'Online',
|
||||||
|
numero_ecommerce: 'EC-987'
|
||||||
|
}]);
|
||||||
|
} finally {
|
||||||
|
delete require.cache[servicePath];
|
||||||
|
if (originalServiceCache) {
|
||||||
|
require.cache[servicePath] = originalServiceCache;
|
||||||
|
}
|
||||||
|
if (originalDbCache) {
|
||||||
|
require.cache[dbPath] = originalDbCache;
|
||||||
|
} else {
|
||||||
|
delete require.cache[dbPath];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const insert = queries.find(query => query.sql.includes('INSERT INTO orders'));
|
||||||
|
|
||||||
|
assert.ok(insert);
|
||||||
|
assert.match(insert.sql, /cliente_nome_fantasia/);
|
||||||
|
assert.match(insert.sql, /id_vendedor/);
|
||||||
|
assert.match(insert.sql, /nome_vendedor/);
|
||||||
|
assert.match(insert.sql, /marketplace/);
|
||||||
|
assert.match(insert.sql, /canal_venda/);
|
||||||
|
assert.match(insert.sql, /numero_ecommerce/);
|
||||||
|
assert.match(insert.sql, /cliente_nome_fantasia = EXCLUDED\.cliente_nome_fantasia/);
|
||||||
|
assert.equal(insert.params.length, 16);
|
||||||
|
assert.deepEqual(insert.params.slice(10), [
|
||||||
|
'Cliente Fantasia',
|
||||||
|
'VEN-1',
|
||||||
|
'Maria',
|
||||||
|
'Mercado Livre',
|
||||||
|
'Online',
|
||||||
|
'EC-987'
|
||||||
|
]);
|
||||||
|
});
|
||||||
@@ -3,7 +3,7 @@ import { useParams, Link, useOutletContext } from 'react-router-dom';
|
|||||||
import { ArrowLeft, User, Tag, Package, DollarSign, Clock, Phone, ChevronLeft, ChevronRight, ShoppingBag, ReceiptText } from 'lucide-react';
|
import { ArrowLeft, User, Tag, Package, DollarSign, Clock, Phone, ChevronLeft, ChevronRight, ShoppingBag, ReceiptText } from 'lucide-react';
|
||||||
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer } from 'recharts';
|
||||||
import DateRangePicker from '../components/DateRangePicker';
|
import DateRangePicker from '../components/DateRangePicker';
|
||||||
import type { ClientDetailsAnalytics, DateRange } from '../types';
|
import type { ClientDetailsAnalytics, DateRange, OrderData } from '../types';
|
||||||
import { fetchClientDetailsAnalytics } from '../dataService';
|
import { fetchClientDetailsAnalytics } from '../dataService';
|
||||||
|
|
||||||
type CustomTooltipProps = {
|
type CustomTooltipProps = {
|
||||||
@@ -26,6 +26,21 @@ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getOrderMetadata = (order: OrderData) => {
|
||||||
|
const seller = [
|
||||||
|
order.nome_vendedor,
|
||||||
|
order.id_vendedor ? `#${order.id_vendedor}` : ''
|
||||||
|
].filter(Boolean).join(' ');
|
||||||
|
|
||||||
|
return [
|
||||||
|
order.cliente_nome_fantasia ? `Fantasia: ${order.cliente_nome_fantasia}` : '',
|
||||||
|
seller ? `Vendedor: ${seller}` : '',
|
||||||
|
order.marketplace ? `Marketplace: ${order.marketplace}` : '',
|
||||||
|
order.canal_venda ? `Canal: ${order.canal_venda}` : '',
|
||||||
|
order.numero_ecommerce ? `E-commerce: ${order.numero_ecommerce}` : ''
|
||||||
|
].filter(Boolean);
|
||||||
|
};
|
||||||
|
|
||||||
const ClientDetails = () => {
|
const ClientDetails = () => {
|
||||||
const { clientToken } = useParams<{ clientToken: string }>();
|
const { clientToken } = useParams<{ clientToken: string }>();
|
||||||
const decodedClientToken = clientToken ? decodeURIComponent(clientToken) : '';
|
const decodedClientToken = clientToken ? decodeURIComponent(clientToken) : '';
|
||||||
@@ -242,7 +257,10 @@ const ClientDetails = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="divide-y divide-zinc-100 dark:divide-dark-border">
|
<div className="divide-y divide-zinc-100 dark:divide-dark-border">
|
||||||
{group.items.map((order, index) => (
|
{group.items.map((order, index) => {
|
||||||
|
const metadata = getOrderMetadata(order);
|
||||||
|
|
||||||
|
return (
|
||||||
<div key={`${order.ID_Produto}-${index}`} className="px-4 py-2 flex flex-col md:flex-row md:items-center justify-between gap-3 hover:bg-zinc-50/50 dark:hover:bg-dark-input/30 transition-colors">
|
<div key={`${order.ID_Produto}-${index}`} className="px-4 py-2 flex flex-col md:flex-row md:items-center justify-between gap-3 hover:bg-zinc-50/50 dark:hover:bg-dark-input/30 transition-colors">
|
||||||
<div className="flex-1 min-w-0">
|
<div className="flex-1 min-w-0">
|
||||||
<div className="flex items-center gap-2 mb-0.5">
|
<div className="flex items-center gap-2 mb-0.5">
|
||||||
@@ -271,6 +289,15 @@ const ClientDetails = () => {
|
|||||||
<span className="text-zinc-500 dark:text-dark-muted font-medium">Preço: <span className="text-zinc-900 dark:text-dark-text font-bold">{formatCurrency(order.Valor_Unitario)}</span></span>
|
<span className="text-zinc-500 dark:text-dark-muted font-medium">Preço: <span className="text-zinc-900 dark:text-dark-text font-bold">{formatCurrency(order.Valor_Unitario)}</span></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{metadata.length > 0 && (
|
||||||
|
<div className="mt-2 flex flex-wrap gap-1.5">
|
||||||
|
{metadata.map(value => (
|
||||||
|
<span key={value} className="max-w-full break-all rounded-md border border-zinc-200 dark:border-dark-border px-2 py-0.5 text-[10px] font-bold text-zinc-500 dark:text-dark-muted">
|
||||||
|
{value}
|
||||||
|
</span>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="text-right shrink-0">
|
<div className="text-right shrink-0">
|
||||||
@@ -278,7 +305,8 @@ const ClientDetails = () => {
|
|||||||
<p className="text-base font-bold text-zinc-900 dark:text-dark-text">{formatCurrency(order.Quantidade * order.Valor_Unitario)}</p>
|
<p className="text-base font-bold text-zinc-900 dark:text-dark-text">{formatCurrency(order.Quantidade * order.Valor_Unitario)}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -9,6 +9,12 @@ export interface OrderData {
|
|||||||
Recebido_Em?: string;
|
Recebido_Em?: string;
|
||||||
ID_Pedido?: string;
|
ID_Pedido?: string;
|
||||||
Fone_Cliente?: string;
|
Fone_Cliente?: string;
|
||||||
|
cliente_nome_fantasia?: string;
|
||||||
|
id_vendedor?: string;
|
||||||
|
nome_vendedor?: string;
|
||||||
|
marketplace?: string;
|
||||||
|
canal_venda?: string;
|
||||||
|
numero_ecommerce?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface StockData {
|
export interface StockData {
|
||||||
|
|||||||
Reference in New Issue
Block a user