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:
@@ -1,7 +1,14 @@
|
||||
import { Modal, Tag, Skeleton, Typography, Row, Col, Statistic, Divider } from 'antd';
|
||||
import { BarcodeOutlined, InboxOutlined, TagsOutlined } from '@ant-design/icons';
|
||||
import {
|
||||
BarcodeOutlined,
|
||||
InboxOutlined,
|
||||
PercentageOutlined,
|
||||
TagsOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import type { PautaPreco } from '@sar/api-interface';
|
||||
import { useProdutoDetail } from '../../lib/queries/catalog';
|
||||
import { useCondicoesComerciais, type CondicaoComercial } from '../../lib/condicoes-comerciais';
|
||||
import { CondicaoTag } from '../../components/CondicaoTag';
|
||||
|
||||
const { Text, Title } = Typography;
|
||||
|
||||
@@ -168,6 +175,56 @@ function PautaPrecoList({ items }: { items: PautaPreco[] }) {
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Condições comerciais vigentes ────────────────────────────────────────────
|
||||
|
||||
function fmtData(iso: string): string {
|
||||
const [y, m, d] = iso.split('-');
|
||||
return `${d}/${m}/${y}`;
|
||||
}
|
||||
|
||||
function CondicoesList({ conds }: { conds: CondicaoComercial[] }) {
|
||||
return (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
{conds.map((c, i) => (
|
||||
<div
|
||||
key={i}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: 12,
|
||||
padding: '10px 14px',
|
||||
borderRadius: 8,
|
||||
background: c.tipo === 'promocao' ? '#FFFBE6' : '#F0F5FF',
|
||||
border: `1px solid ${c.tipo === 'promocao' ? '#FFE58F' : '#ADC6FF'}`,
|
||||
}}
|
||||
>
|
||||
<div style={{ minWidth: 0 }}>
|
||||
<Text style={{ fontSize: 11, color: '#94A3B8', display: 'block', lineHeight: 1.2 }}>
|
||||
{c.tipo === 'promocao' ? 'Promoção' : 'Condição especial'} · válida até{' '}
|
||||
{fmtData(c.dataFim)}
|
||||
</Text>
|
||||
<Text strong style={{ fontSize: 13, color: '#1F2937' }}>
|
||||
{c.descricao}
|
||||
</Text>
|
||||
</div>
|
||||
<Text
|
||||
strong
|
||||
style={{
|
||||
fontSize: 16,
|
||||
color: c.tipo === 'promocao' ? '#D48806' : '#2F54EB',
|
||||
fontVariantNumeric: 'tabular-nums',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{c.descPct.toLocaleString('pt-BR')}% desc.
|
||||
</Text>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// ─── Modal principal ──────────────────────────────────────────────────────────
|
||||
|
||||
interface Props {
|
||||
@@ -177,6 +234,8 @@ interface Props {
|
||||
|
||||
export function ProductDetailDrawer({ idErp, onClose }: Props) {
|
||||
const { data, isLoading } = useProdutoDetail(idErp ?? undefined);
|
||||
const condicoesDe = useCondicoesComerciais();
|
||||
const conds = data ? condicoesDe(data) : [];
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -227,6 +286,7 @@ export function ProductDetailDrawer({ idErp, onClose }: Props) {
|
||||
justifyContent: 'flex-end',
|
||||
}}
|
||||
>
|
||||
<CondicaoTag conds={conds} />
|
||||
{data.marca && <Tag color="blue">{data.marca.trim()}</Tag>}
|
||||
{data.unidade && <Tag>{data.unidade}</Tag>}
|
||||
<Tag color={data.ativo ? 'green' : 'red'}>{data.ativo ? 'Ativo' : 'Inativo'}</Tag>
|
||||
@@ -239,6 +299,13 @@ export function ProductDetailDrawer({ idErp, onClose }: Props) {
|
||||
<Row gutter={40}>
|
||||
{/* Coluna esquerda */}
|
||||
<Col xs={24} md={14}>
|
||||
{/* Condições comerciais vigentes */}
|
||||
{conds.length > 0 && (
|
||||
<Section icon={<PercentageOutlined />} title="Condições Comerciais">
|
||||
<CondicoesList conds={conds} />
|
||||
</Section>
|
||||
)}
|
||||
|
||||
{/* Classificação */}
|
||||
<Section icon={<TagsOutlined />} title="Classificação">
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '0 24px' }}>
|
||||
|
||||
Reference in New Issue
Block a user