feat(politicas): regras de desconto por grupo/subgrupo/rep e condicoes comerciais no catalogo
- Nova tabela sar.regras_desconto (grupo, subgrupo, representante, % e vigencia; campos nulos = todos) com migration idempotente, espelho no sar-erp-schema.sql e GRANT no provision-client - CRUD manager-only em /politicas/regras + aba "Regras de Desconto" na tela de Politicas Comerciais (selects com nomes de rep/grupo/subgrupo via /politicas/grupos-produto) - GET /politicas/regras/vigentes (rep ve so as que o alcancam) e GET /politicas/promocoes/vigentes para o catalogo - Regra vigente pre-aplica o desconto no carrinho do novo pedido - Selos "Promocao"/"Cond. especial" na busca de produto, catalogo do pedido e pagina de catalogo; secao "Condicoes Comerciais" no modal de detalhe do produto (descricao, % e validade) - Empty imageStyle deprecado -> styles.image no carrinho Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,8 @@ import { EyeOutlined } from '@ant-design/icons';
|
||||
import type { TableColumnsType } from 'antd';
|
||||
import type { ProdutoSummary } from '@sar/api-interface';
|
||||
import { useCatalog, usePautas } from '../../lib/queries/catalog';
|
||||
import { useCondicoesComerciais, type CondicaoComercial } from '../../lib/condicoes-comerciais';
|
||||
import { CondicaoTag } from '../../components/CondicaoTag';
|
||||
import { ProductDetailDrawer } from './ProductDetailDrawer';
|
||||
|
||||
const { useBreakpoint } = Grid;
|
||||
@@ -16,7 +18,10 @@ function fmtPrice(v: string | null | undefined): string {
|
||||
return n > 0 ? n.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }) : '—';
|
||||
}
|
||||
|
||||
function buildColumns(onDetail: (id: number) => void): TableColumnsType<ProdutoSummary> {
|
||||
function buildColumns(
|
||||
onDetail: (id: number) => void,
|
||||
condicoesDe: (p: ProdutoSummary) => CondicaoComercial[],
|
||||
): TableColumnsType<ProdutoSummary> {
|
||||
return [
|
||||
{
|
||||
title: 'Código',
|
||||
@@ -29,7 +34,18 @@ function buildColumns(onDetail: (id: number) => void): TableColumnsType<ProdutoS
|
||||
dataIndex: 'descricao',
|
||||
render: (v: string, row: ProdutoSummary) => (
|
||||
<div>
|
||||
<div style={{ fontWeight: 500 }}>{v.trim()}</div>
|
||||
<div
|
||||
style={{
|
||||
fontWeight: 500,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 6,
|
||||
flexWrap: 'wrap',
|
||||
}}
|
||||
>
|
||||
{v.trim()}
|
||||
<CondicaoTag conds={condicoesDe(row)} compact />
|
||||
</div>
|
||||
{row.grupo && <div style={{ fontSize: 12, color: '#888' }}>{row.grupo.trim()}</div>}
|
||||
</div>
|
||||
),
|
||||
@@ -101,8 +117,9 @@ export function CatalogPage() {
|
||||
|
||||
const { data: pautas, isLoading: pautasLoading } = usePautas();
|
||||
const { data, isLoading } = useCatalog({ q: q || undefined, idPauta, page, limit });
|
||||
const condicoesDe = useCondicoesComerciais();
|
||||
|
||||
const columns = buildColumns((id) => setSelectedIdErp(id));
|
||||
const columns = buildColumns((id) => setSelectedIdErp(id), condicoesDe);
|
||||
|
||||
return (
|
||||
<div style={{ maxWidth: 1400, margin: '0 auto' }}>
|
||||
|
||||
Reference in New Issue
Block a user