-- =====================================================
-- SCHEMA: sales
-- PROPÓSITO: Gestión de ventas, cotizaciones, clientes
-- MÓDULOS: MGN-007 (Ventas Básico)
-- FECHA: 2025-11-24
-- =====================================================

-- Crear schema
CREATE SCHEMA IF NOT EXISTS sales;

-- =====================================================
-- TYPES (ENUMs)
-- =====================================================

CREATE TYPE sales.order_status AS ENUM (
  'draft',
  'sent',
  'sale',
  'done',
  'cancelled'
);

CREATE TYPE sales.quotation_status AS ENUM (
  'draft',
  'sent',
  'approved',
  'rejected',
  'converted',
  'expired'
);

CREATE TYPE sales.invoice_policy AS ENUM (
  'order',
  'delivery'
);

CREATE TYPE sales.delivery_status AS ENUM (
  'pending',
  'partial',
  'delivered'
);

CREATE TYPE sales.invoice_status AS ENUM (
  'pending',
  'partial',
  'invoiced'
);

-- =====================================================
-- TABLES
-- =====================================================

-- Tabla: sales_orders (Órdenes de venta)
CREATE TABLE sales.sales_orders (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,

  -- Numeración
  name VARCHAR(100) NOT NULL,
  client_order_ref VARCHAR(100), -- Referencia del cliente

  -- Cliente
  partner_id UUID NOT NULL REFERENCES core.partners(id),

  -- COR-010: Direcciones de facturación y envío separadas
  partner_invoice_id UUID REFERENCES core.partners(id),
  partner_shipping_id UUID REFERENCES core.partners(id),

  -- Fechas
  order_date DATE NOT NULL,
  validity_date DATE,
  commitment_date DATE,

  -- Configuración
  currency_id UUID NOT NULL REFERENCES core.currencies(id),
  pricelist_id UUID REFERENCES sales.pricelists(id),
  payment_term_id UUID REFERENCES financial.payment_terms(id),

  -- Usuario
  user_id UUID REFERENCES auth.users(id),
  sales_team_id UUID REFERENCES sales.sales_teams(id),

  -- Montos
  amount_untaxed DECIMAL(15, 2) NOT NULL DEFAULT 0,
  amount_tax DECIMAL(15, 2) NOT NULL DEFAULT 0,
  amount_total DECIMAL(15, 2) NOT NULL DEFAULT 0,

  -- Estado
  status sales.order_status NOT NULL DEFAULT 'draft',
  invoice_status sales.invoice_status NOT NULL DEFAULT 'pending',
  delivery_status sales.delivery_status NOT NULL DEFAULT 'pending',

  -- Facturación
  invoice_policy sales.invoice_policy DEFAULT 'order',

  -- Relaciones generadas
  picking_id UUID REFERENCES inventory.pickings(id),

  -- COR-006: Vinculación con facturas
  invoice_ids UUID[] DEFAULT '{}',
  invoice_count INTEGER DEFAULT 0,

  -- COR-011: Bloqueo de orden
  locked BOOLEAN DEFAULT FALSE,

  -- COR-012: Anticipos (Downpayments)
  require_signature BOOLEAN DEFAULT FALSE,
  require_payment BOOLEAN DEFAULT FALSE,
  prepayment_percent DECIMAL(5, 2) DEFAULT 0,

  -- Notas
  notes TEXT,
  terms_conditions TEXT,

  -- Firma electrónica
  signature TEXT, -- base64
  signed_by VARCHAR(255),  -- COR-012: Nombre del firmante
  signature_date TIMESTAMP,
  signature_ip INET,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),
  confirmed_at TIMESTAMP,
  confirmed_by UUID REFERENCES auth.users(id),
  cancelled_at TIMESTAMP,
  cancelled_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_sales_orders_name_company UNIQUE (company_id, name),
  CONSTRAINT chk_sales_orders_validity CHECK (validity_date IS NULL OR validity_date >= order_date)
);

-- Tabla: sales_order_lines (Líneas de orden de venta)
CREATE TABLE sales.sales_order_lines (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  order_id UUID NOT NULL REFERENCES sales.sales_orders(id) ON DELETE CASCADE,

  product_id UUID NOT NULL REFERENCES inventory.products(id),
  description TEXT NOT NULL,

  -- Cantidades
  quantity DECIMAL(12, 4) NOT NULL,
  qty_delivered DECIMAL(12, 4) DEFAULT 0,
  qty_invoiced DECIMAL(12, 4) DEFAULT 0,
  uom_id UUID NOT NULL REFERENCES core.uom(id),

  -- Precios
  price_unit DECIMAL(15, 4) NOT NULL,
  discount DECIMAL(5, 2) DEFAULT 0, -- Porcentaje de descuento

  -- Impuestos
  tax_ids UUID[] DEFAULT '{}',

  -- Montos
  amount_untaxed DECIMAL(15, 2) NOT NULL,
  amount_tax DECIMAL(15, 2) NOT NULL,
  amount_total DECIMAL(15, 2) NOT NULL,

  -- Analítica
  analytic_account_id UUID REFERENCES analytics.analytic_accounts(id), -- Distribución analítica

  -- COR-012: Soporte para anticipos
  is_downpayment BOOLEAN DEFAULT FALSE,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP,

  CONSTRAINT chk_sales_order_lines_quantity CHECK (quantity > 0),
  CONSTRAINT chk_sales_order_lines_discount CHECK (discount >= 0 AND discount <= 100),
  CONSTRAINT chk_sales_order_lines_qty_delivered CHECK (qty_delivered >= 0 AND qty_delivered <= quantity),
  CONSTRAINT chk_sales_order_lines_qty_invoiced CHECK (qty_invoiced >= 0 AND qty_invoiced <= quantity)
);

-- Índices para sales_order_lines
CREATE INDEX idx_sales_order_lines_tenant_id ON sales.sales_order_lines(tenant_id);
CREATE INDEX idx_sales_order_lines_order_id ON sales.sales_order_lines(order_id);
CREATE INDEX idx_sales_order_lines_product_id ON sales.sales_order_lines(product_id);

-- RLS para sales_order_lines
ALTER TABLE sales.sales_order_lines ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_sales_order_lines ON sales.sales_order_lines
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Tabla: quotations (Cotizaciones)
CREATE TABLE sales.quotations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,

  -- Numeración
  name VARCHAR(100) NOT NULL,

  -- Cliente potencial
  partner_id UUID NOT NULL REFERENCES core.partners(id),

  -- Fechas
  quotation_date DATE NOT NULL,
  validity_date DATE NOT NULL,

  -- Configuración
  currency_id UUID NOT NULL REFERENCES core.currencies(id),
  pricelist_id UUID REFERENCES sales.pricelists(id),

  -- Usuario
  user_id UUID REFERENCES auth.users(id),
  sales_team_id UUID REFERENCES sales.sales_teams(id),

  -- Montos
  amount_untaxed DECIMAL(15, 2) NOT NULL DEFAULT 0,
  amount_tax DECIMAL(15, 2) NOT NULL DEFAULT 0,
  amount_total DECIMAL(15, 2) NOT NULL DEFAULT 0,

  -- Estado
  status sales.quotation_status NOT NULL DEFAULT 'draft',

  -- Conversión
  sale_order_id UUID REFERENCES sales.sales_orders(id), -- Orden generada

  -- Notas
  notes TEXT,
  terms_conditions TEXT,

  -- Firma electrónica
  signature TEXT, -- base64
  signature_date TIMESTAMP,
  signature_ip INET,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_quotations_name_company UNIQUE (company_id, name),
  CONSTRAINT chk_quotations_validity CHECK (validity_date >= quotation_date)
);

-- Tabla: quotation_lines (Líneas de cotización)
CREATE TABLE sales.quotation_lines (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  quotation_id UUID NOT NULL REFERENCES sales.quotations(id) ON DELETE CASCADE,

  product_id UUID REFERENCES inventory.products(id),
  description TEXT NOT NULL,
  quantity DECIMAL(12, 4) NOT NULL,
  uom_id UUID NOT NULL REFERENCES core.uom(id),
  price_unit DECIMAL(15, 4) NOT NULL,
  discount DECIMAL(5, 2) DEFAULT 0,
  tax_ids UUID[] DEFAULT '{}',
  amount_untaxed DECIMAL(15, 2) NOT NULL,
  amount_tax DECIMAL(15, 2) NOT NULL,
  amount_total DECIMAL(15, 2) NOT NULL,

  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  CONSTRAINT chk_quotation_lines_quantity CHECK (quantity > 0),
  CONSTRAINT chk_quotation_lines_discount CHECK (discount >= 0 AND discount <= 100)
);

-- Índices para quotation_lines
CREATE INDEX idx_quotation_lines_tenant_id ON sales.quotation_lines(tenant_id);
CREATE INDEX idx_quotation_lines_quotation_id ON sales.quotation_lines(quotation_id);
CREATE INDEX idx_quotation_lines_product_id ON sales.quotation_lines(product_id);

-- RLS para quotation_lines
ALTER TABLE sales.quotation_lines ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_quotation_lines ON sales.quotation_lines
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Tabla: pricelists (Listas de precios)
CREATE TABLE sales.pricelists (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID REFERENCES auth.companies(id),

  name VARCHAR(255) NOT NULL,
  currency_id UUID NOT NULL REFERENCES core.currencies(id),

  -- Control
  active BOOLEAN NOT NULL DEFAULT TRUE,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_pricelists_name_tenant UNIQUE (tenant_id, name)
);

-- Tabla: pricelist_items (Items de lista de precios)
CREATE TABLE sales.pricelist_items (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  pricelist_id UUID NOT NULL REFERENCES sales.pricelists(id) ON DELETE CASCADE,

  product_id UUID REFERENCES inventory.products(id),
  product_category_id UUID REFERENCES core.product_categories(id),

  -- Precio
  price DECIMAL(15, 4) NOT NULL,

  -- Cantidad mínima
  min_quantity DECIMAL(12, 4) DEFAULT 1,

  -- Validez
  valid_from DATE,
  valid_to DATE,

  -- Control
  active BOOLEAN NOT NULL DEFAULT TRUE,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),

  CONSTRAINT chk_pricelist_items_price CHECK (price >= 0),
  CONSTRAINT chk_pricelist_items_min_qty CHECK (min_quantity > 0),
  CONSTRAINT chk_pricelist_items_dates CHECK (valid_to IS NULL OR valid_to >= valid_from),
  CONSTRAINT chk_pricelist_items_product_or_category CHECK (
    (product_id IS NOT NULL AND product_category_id IS NULL) OR
    (product_id IS NULL AND product_category_id IS NOT NULL)
  )
);

-- Índices para pricelist_items
CREATE INDEX idx_pricelist_items_tenant_id ON sales.pricelist_items(tenant_id);
CREATE INDEX idx_pricelist_items_pricelist_id ON sales.pricelist_items(pricelist_id);
CREATE INDEX idx_pricelist_items_product_id ON sales.pricelist_items(product_id);

-- RLS para pricelist_items
ALTER TABLE sales.pricelist_items ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_pricelist_items ON sales.pricelist_items
  USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);

-- Tabla: customer_groups (Grupos de clientes)
CREATE TABLE sales.customer_groups (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  description TEXT,
  discount_percentage DECIMAL(5, 2) DEFAULT 0,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_customer_groups_name_tenant UNIQUE (tenant_id, name),
  CONSTRAINT chk_customer_groups_discount CHECK (discount_percentage >= 0 AND discount_percentage <= 100)
);

-- Tabla: customer_group_members (Miembros de grupos)
CREATE TABLE sales.customer_group_members (
  customer_group_id UUID NOT NULL REFERENCES sales.customer_groups(id) ON DELETE CASCADE,
  partner_id UUID NOT NULL REFERENCES core.partners(id) ON DELETE CASCADE,
  joined_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (customer_group_id, partner_id)
);

-- Tabla: sales_teams (Equipos de ventas)
CREATE TABLE sales.sales_teams (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  company_id UUID NOT NULL REFERENCES auth.companies(id) ON DELETE CASCADE,

  name VARCHAR(255) NOT NULL,
  code VARCHAR(50),
  team_leader_id UUID REFERENCES auth.users(id),

  -- Objetivos
  target_monthly DECIMAL(15, 2),
  target_annual DECIMAL(15, 2),

  -- Control
  active BOOLEAN NOT NULL DEFAULT TRUE,

  -- Auditoría
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  created_by UUID REFERENCES auth.users(id),
  updated_at TIMESTAMP,
  updated_by UUID REFERENCES auth.users(id),

  CONSTRAINT uq_sales_teams_code_company UNIQUE (company_id, code)
);

-- Tabla: sales_team_members (Miembros de equipos)
CREATE TABLE sales.sales_team_members (
  sales_team_id UUID NOT NULL REFERENCES sales.sales_teams(id) ON DELETE CASCADE,
  user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
  joined_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,

  PRIMARY KEY (sales_team_id, user_id)
);

-- =====================================================
-- INDICES
-- =====================================================

-- Sales Orders
CREATE INDEX idx_sales_orders_tenant_id ON sales.sales_orders(tenant_id);
CREATE INDEX idx_sales_orders_company_id ON sales.sales_orders(company_id);
CREATE INDEX idx_sales_orders_partner_id ON sales.sales_orders(partner_id);
CREATE INDEX idx_sales_orders_name ON sales.sales_orders(name);
CREATE INDEX idx_sales_orders_status ON sales.sales_orders(status);
CREATE INDEX idx_sales_orders_order_date ON sales.sales_orders(order_date);
CREATE INDEX idx_sales_orders_user_id ON sales.sales_orders(user_id);
CREATE INDEX idx_sales_orders_sales_team_id ON sales.sales_orders(sales_team_id);

-- Sales Order Lines
CREATE INDEX idx_sales_order_lines_order_id ON sales.sales_order_lines(order_id);
CREATE INDEX idx_sales_order_lines_product_id ON sales.sales_order_lines(product_id);
CREATE INDEX idx_sales_order_lines_analytic_account_id ON sales.sales_order_lines(analytic_account_id) WHERE analytic_account_id IS NOT NULL;

-- Quotations
CREATE INDEX idx_quotations_tenant_id ON sales.quotations(tenant_id);
CREATE INDEX idx_quotations_company_id ON sales.quotations(company_id);
CREATE INDEX idx_quotations_partner_id ON sales.quotations(partner_id);
CREATE INDEX idx_quotations_status ON sales.quotations(status);
CREATE INDEX idx_quotations_validity_date ON sales.quotations(validity_date);

-- Quotation Lines
CREATE INDEX idx_quotation_lines_quotation_id ON sales.quotation_lines(quotation_id);
CREATE INDEX idx_quotation_lines_product_id ON sales.quotation_lines(product_id);

-- Pricelists
CREATE INDEX idx_pricelists_tenant_id ON sales.pricelists(tenant_id);
CREATE INDEX idx_pricelists_active ON sales.pricelists(active) WHERE active = TRUE;

-- Pricelist Items
CREATE INDEX idx_pricelist_items_pricelist_id ON sales.pricelist_items(pricelist_id);
CREATE INDEX idx_pricelist_items_product_id ON sales.pricelist_items(product_id);
CREATE INDEX idx_pricelist_items_category_id ON sales.pricelist_items(product_category_id);

-- Customer Groups
CREATE INDEX idx_customer_groups_tenant_id ON sales.customer_groups(tenant_id);

-- Sales Teams
CREATE INDEX idx_sales_teams_tenant_id ON sales.sales_teams(tenant_id);
CREATE INDEX idx_sales_teams_company_id ON sales.sales_teams(company_id);
CREATE INDEX idx_sales_teams_leader_id ON sales.sales_teams(team_leader_id);

-- =====================================================
-- FUNCTIONS
-- =====================================================

-- Función: calculate_sales_order_totals
CREATE OR REPLACE FUNCTION sales.calculate_sales_order_totals(p_order_id UUID)
RETURNS VOID AS $$
DECLARE
  v_amount_untaxed DECIMAL;
  v_amount_tax DECIMAL;
  v_amount_total DECIMAL;
BEGIN
  SELECT
    COALESCE(SUM(amount_untaxed), 0),
    COALESCE(SUM(amount_tax), 0),
    COALESCE(SUM(amount_total), 0)
  INTO v_amount_untaxed, v_amount_tax, v_amount_total
  FROM sales.sales_order_lines
  WHERE order_id = p_order_id;

  UPDATE sales.sales_orders
  SET amount_untaxed = v_amount_untaxed,
      amount_tax = v_amount_tax,
      amount_total = v_amount_total,
      updated_at = CURRENT_TIMESTAMP,
      updated_by = get_current_user_id()
  WHERE id = p_order_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION sales.calculate_sales_order_totals IS 'Calcula los totales de una orden de venta';

-- Función: calculate_quotation_totals
CREATE OR REPLACE FUNCTION sales.calculate_quotation_totals(p_quotation_id UUID)
RETURNS VOID AS $$
DECLARE
  v_amount_untaxed DECIMAL;
  v_amount_tax DECIMAL;
  v_amount_total DECIMAL;
BEGIN
  SELECT
    COALESCE(SUM(amount_untaxed), 0),
    COALESCE(SUM(amount_tax), 0),
    COALESCE(SUM(amount_total), 0)
  INTO v_amount_untaxed, v_amount_tax, v_amount_total
  FROM sales.quotation_lines
  WHERE quotation_id = p_quotation_id;

  UPDATE sales.quotations
  SET amount_untaxed = v_amount_untaxed,
      amount_tax = v_amount_tax,
      amount_total = v_amount_total,
      updated_at = CURRENT_TIMESTAMP,
      updated_by = get_current_user_id()
  WHERE id = p_quotation_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION sales.calculate_quotation_totals IS 'Calcula los totales de una cotización';

-- Función: convert_quotation_to_order
CREATE OR REPLACE FUNCTION sales.convert_quotation_to_order(p_quotation_id UUID)
RETURNS UUID AS $$
DECLARE
  v_quotation RECORD;
  v_order_id UUID;
BEGIN
  -- Obtener cotización
  SELECT * INTO v_quotation
  FROM sales.quotations
  WHERE id = p_quotation_id;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'Quotation % not found', p_quotation_id;
  END IF;

  IF v_quotation.status != 'approved' THEN
    RAISE EXCEPTION 'Quotation must be approved before conversion';
  END IF;

  -- Crear orden de venta
  INSERT INTO sales.sales_orders (
    tenant_id,
    company_id,
    name,
    partner_id,
    order_date,
    currency_id,
    pricelist_id,
    user_id,
    sales_team_id,
    amount_untaxed,
    amount_tax,
    amount_total,
    notes,
    terms_conditions,
    signature,
    signature_date,
    signature_ip
  ) VALUES (
    v_quotation.tenant_id,
    v_quotation.company_id,
    REPLACE(v_quotation.name, 'QT', 'SO'),
    v_quotation.partner_id,
    CURRENT_DATE,
    v_quotation.currency_id,
    v_quotation.pricelist_id,
    v_quotation.user_id,
    v_quotation.sales_team_id,
    v_quotation.amount_untaxed,
    v_quotation.amount_tax,
    v_quotation.amount_total,
    v_quotation.notes,
    v_quotation.terms_conditions,
    v_quotation.signature,
    v_quotation.signature_date,
    v_quotation.signature_ip
  ) RETURNING id INTO v_order_id;

  -- Copiar líneas
  INSERT INTO sales.sales_order_lines (
    order_id,
    product_id,
    description,
    quantity,
    uom_id,
    price_unit,
    discount,
    tax_ids,
    amount_untaxed,
    amount_tax,
    amount_total
  )
  SELECT
    v_order_id,
    product_id,
    description,
    quantity,
    uom_id,
    price_unit,
    discount,
    tax_ids,
    amount_untaxed,
    amount_tax,
    amount_total
  FROM sales.quotation_lines
  WHERE quotation_id = p_quotation_id;

  -- Actualizar cotización
  UPDATE sales.quotations
  SET status = 'converted',
      sale_order_id = v_order_id,
      updated_at = CURRENT_TIMESTAMP,
      updated_by = get_current_user_id()
  WHERE id = p_quotation_id;

  RETURN v_order_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION sales.convert_quotation_to_order IS 'Convierte una cotización aprobada en orden de venta';

-- =====================================================
-- TRIGGERS
-- =====================================================

CREATE TRIGGER trg_sales_orders_updated_at
  BEFORE UPDATE ON sales.sales_orders
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_quotations_updated_at
  BEFORE UPDATE ON sales.quotations
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_pricelists_updated_at
  BEFORE UPDATE ON sales.pricelists
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

CREATE TRIGGER trg_sales_teams_updated_at
  BEFORE UPDATE ON sales.sales_teams
  FOR EACH ROW
  EXECUTE FUNCTION auth.update_updated_at_column();

-- Trigger: Actualizar totales de orden al cambiar líneas
CREATE OR REPLACE FUNCTION sales.trg_update_so_totals()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'DELETE' THEN
    PERFORM sales.calculate_sales_order_totals(OLD.order_id);
  ELSE
    PERFORM sales.calculate_sales_order_totals(NEW.order_id);
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_sales_order_lines_update_totals
  AFTER INSERT OR UPDATE OR DELETE ON sales.sales_order_lines
  FOR EACH ROW
  EXECUTE FUNCTION sales.trg_update_so_totals();

-- Trigger: Actualizar totales de cotización al cambiar líneas
CREATE OR REPLACE FUNCTION sales.trg_update_quotation_totals()
RETURNS TRIGGER AS $$
BEGIN
  IF TG_OP = 'DELETE' THEN
    PERFORM sales.calculate_quotation_totals(OLD.quotation_id);
  ELSE
    PERFORM sales.calculate_quotation_totals(NEW.quotation_id);
  END IF;
  RETURN NULL;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trg_quotation_lines_update_totals
  AFTER INSERT OR UPDATE OR DELETE ON sales.quotation_lines
  FOR EACH ROW
  EXECUTE FUNCTION sales.trg_update_quotation_totals();

-- =====================================================
-- TRACKING AUTOMÁTICO (mail.thread pattern)
-- =====================================================

-- Trigger: Tracking automático para órdenes de venta
CREATE TRIGGER track_sales_order_changes
  AFTER INSERT OR UPDATE OR DELETE ON sales.sales_orders
  FOR EACH ROW EXECUTE FUNCTION system.track_field_changes();

COMMENT ON TRIGGER track_sales_order_changes ON sales.sales_orders IS
'Registra automáticamente cambios en órdenes de venta (estado, cliente, monto, fecha, facturación, entrega)';

-- =====================================================
-- ROW LEVEL SECURITY (RLS)
-- =====================================================

ALTER TABLE sales.sales_orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE sales.quotations ENABLE ROW LEVEL SECURITY;
ALTER TABLE sales.pricelists ENABLE ROW LEVEL SECURITY;
ALTER TABLE sales.customer_groups ENABLE ROW LEVEL SECURITY;
ALTER TABLE sales.sales_teams ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_sales_orders ON sales.sales_orders
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_quotations ON sales.quotations
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_pricelists ON sales.pricelists
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_customer_groups ON sales.customer_groups
  USING (tenant_id = get_current_tenant_id());

CREATE POLICY tenant_isolation_sales_teams ON sales.sales_teams
  USING (tenant_id = get_current_tenant_id());

-- =====================================================
-- COMENTARIOS
-- =====================================================

COMMENT ON SCHEMA sales IS 'Schema de gestión de ventas, cotizaciones y clientes';
COMMENT ON TABLE sales.sales_orders IS 'Órdenes de venta confirmadas';
COMMENT ON TABLE sales.sales_order_lines IS 'Líneas de órdenes de venta';
COMMENT ON TABLE sales.quotations IS 'Cotizaciones enviadas a clientes';
COMMENT ON TABLE sales.quotation_lines IS 'Líneas de cotizaciones';
COMMENT ON TABLE sales.pricelists IS 'Listas de precios para clientes';
COMMENT ON TABLE sales.pricelist_items IS 'Items de listas de precios por producto/categoría';
COMMENT ON TABLE sales.customer_groups IS 'Grupos de clientes para descuentos y segmentación';
COMMENT ON TABLE sales.sales_teams IS 'Equipos de ventas con objetivos';

-- =====================================================
-- COR-033: Sales Order Templates
-- Equivalente a sale.order.template de Odoo
-- =====================================================

CREATE TABLE sales.order_templates (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
  name VARCHAR(255) NOT NULL,
  note TEXT,
  number_of_days INTEGER DEFAULT 0,
  require_signature BOOLEAN DEFAULT FALSE,
  require_payment BOOLEAN DEFAULT FALSE,
  prepayment_percent DECIMAL(5,2) DEFAULT 0,
  is_active BOOLEAN DEFAULT TRUE,
  created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
  updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE sales.order_template_lines (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  template_id UUID NOT NULL REFERENCES sales.order_templates(id) ON DELETE CASCADE,
  sequence INTEGER DEFAULT 10,
  product_id UUID REFERENCES inventory.products(id),
  name TEXT,
  quantity DECIMAL(20,6) DEFAULT 1,
  product_uom_id UUID REFERENCES core.uom(id),
  display_type VARCHAR(20)  -- line_section, line_note
);

CREATE INDEX idx_order_templates_tenant ON sales.order_templates(tenant_id);
CREATE INDEX idx_order_template_lines_template ON sales.order_template_lines(template_id);

-- RLS
ALTER TABLE sales.order_templates ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation_order_templates ON sales.order_templates
  USING (tenant_id = get_current_tenant_id());

COMMENT ON TABLE sales.order_templates IS 'COR-033: Sale order templates - Equivalent to sale.order.template';
COMMENT ON TABLE sales.order_template_lines IS 'COR-033: Sale order template lines';

-- =====================================================
-- COR-048: Sales Order Additional Fields
-- Campos adicionales para alinear con Odoo
-- =====================================================

-- Agregar campos a sales_orders
ALTER TABLE sales.sales_orders
  ADD COLUMN IF NOT EXISTS incoterm_id UUID,  -- FK to financial.incoterms
  ADD COLUMN IF NOT EXISTS incoterm_location VARCHAR(255),
  ADD COLUMN IF NOT EXISTS fiscal_position_id UUID,  -- FK to financial.fiscal_positions
  ADD COLUMN IF NOT EXISTS origin VARCHAR(255),  -- Documento origen
  ADD COLUMN IF NOT EXISTS campaign_id UUID,  -- FK to marketing campaigns
  ADD COLUMN IF NOT EXISTS medium_id UUID,  -- FK to utm.medium
  ADD COLUMN IF NOT EXISTS source_id UUID,  -- FK to utm.source
  ADD COLUMN IF NOT EXISTS opportunity_id UUID,  -- FK to crm.opportunities
  ADD COLUMN IF NOT EXISTS date_order TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP,
  ADD COLUMN IF NOT EXISTS amount_undiscounted DECIMAL(20,6),  -- Amount before discount
  ADD COLUMN IF NOT EXISTS amount_to_invoice DECIMAL(20,6),  -- Pending to invoice
  ADD COLUMN IF NOT EXISTS amount_invoiced DECIMAL(20,6);  -- Already invoiced

-- Agregar campos a sales_order_lines
ALTER TABLE sales.sales_order_lines
  ADD COLUMN IF NOT EXISTS sequence INTEGER DEFAULT 10,
  ADD COLUMN IF NOT EXISTS display_type VARCHAR(20),  -- line_section, line_note
  ADD COLUMN IF NOT EXISTS qty_to_invoice DECIMAL(20,6) GENERATED ALWAYS AS (quantity - qty_invoiced) STORED,
  ADD COLUMN IF NOT EXISTS qty_to_deliver DECIMAL(20,6) GENERATED ALWAYS AS (quantity - qty_delivered) STORED,
  ADD COLUMN IF NOT EXISTS product_packaging_id UUID,
  ADD COLUMN IF NOT EXISTS product_packaging_qty DECIMAL(20,6),
  ADD COLUMN IF NOT EXISTS price_reduce DECIMAL(20,6),  -- Price after discount
  ADD COLUMN IF NOT EXISTS price_reduce_taxexcl DECIMAL(20,6),
  ADD COLUMN IF NOT EXISTS price_reduce_taxinc DECIMAL(20,6),
  ADD COLUMN IF NOT EXISTS customer_lead INTEGER DEFAULT 0,  -- Dias de entrega al cliente
  ADD COLUMN IF NOT EXISTS route_id UUID;  -- FK to inventory.routes

CREATE INDEX idx_sales_orders_origin ON sales.sales_orders(origin);
CREATE INDEX idx_sales_orders_opportunity ON sales.sales_orders(opportunity_id);
CREATE INDEX idx_sales_order_lines_sequence ON sales.sales_order_lines(order_id, sequence);

COMMENT ON COLUMN sales.sales_orders.incoterm_id IS 'COR-048: Incoterm reference';
COMMENT ON COLUMN sales.sales_orders.origin IS 'COR-048: Source document reference';
COMMENT ON COLUMN sales.sales_order_lines.qty_to_invoice IS 'COR-048: Computed quantity to invoice';

-- =====================================================
-- COR-049: Sales Action Confirm
-- Funcion para confirmar SO y crear delivery
-- =====================================================

CREATE OR REPLACE FUNCTION sales.action_confirm(p_order_id UUID)
RETURNS UUID AS $$
DECLARE
  v_order RECORD;
  v_line RECORD;
  v_picking_id UUID;
  v_move_id UUID;
  v_location_stock UUID;
  v_location_customer UUID;
BEGIN
  -- Obtener orden
  SELECT * INTO v_order FROM sales.sales_orders WHERE id = p_order_id;

  IF NOT FOUND THEN
    RAISE EXCEPTION 'Sales order % not found', p_order_id;
  END IF;

  IF v_order.status NOT IN ('draft', 'sent') THEN
    RAISE EXCEPTION 'Sales order % cannot be confirmed from status %', p_order_id, v_order.status;
  END IF;

  -- Obtener ubicaciones
  SELECT id INTO v_location_stock
  FROM inventory.locations
  WHERE location_type = 'internal' AND tenant_id = v_order.tenant_id
  LIMIT 1;

  SELECT id INTO v_location_customer
  FROM inventory.locations
  WHERE location_type = 'customer' AND tenant_id = v_order.tenant_id
  LIMIT 1;

  -- Crear picking de salida
  INSERT INTO inventory.pickings (
    tenant_id, company_id, name, picking_type,
    location_id, location_dest_id, partner_id,
    origin, scheduled_date, status
  ) VALUES (
    v_order.tenant_id, v_order.company_id,
    'OUT/' || v_order.name,
    'outgoing',
    v_location_stock, v_location_customer,
    v_order.partner_id,
    v_order.name,
    COALESCE(v_order.commitment_date, CURRENT_DATE + 1),
    'draft'
  ) RETURNING id INTO v_picking_id;

  -- Crear stock moves para cada linea
  FOR v_line IN
    SELECT * FROM sales.sales_order_lines
    WHERE order_id = p_order_id AND display_type IS NULL
  LOOP
    INSERT INTO inventory.stock_moves (
      tenant_id, picking_id, product_id,
      product_uom_id, product_qty,
      location_id, location_dest_id,
      origin, state, name
    ) VALUES (
      v_order.tenant_id, v_picking_id, v_line.product_id,
      v_line.uom_id, v_line.quantity,
      v_location_stock, v_location_customer,
      v_order.name, 'draft', v_line.description
    ) RETURNING id INTO v_move_id;
  END LOOP;

  -- Actualizar orden
  UPDATE sales.sales_orders
  SET status = 'sale',
      picking_id = v_picking_id,
      confirmed_at = NOW(),
      updated_at = NOW()
  WHERE id = p_order_id;

  RETURN v_picking_id;
END;
$$ LANGUAGE plpgsql;

COMMENT ON FUNCTION sales.action_confirm IS 'COR-049: Confirm sales order and create delivery picking';

-- =====================================================
-- COR-050: Product Pricelist Compute
-- Funcion para calcular precio desde pricelist
-- =====================================================

CREATE OR REPLACE FUNCTION sales.get_pricelist_price(
  p_pricelist_id UUID,
  p_product_id UUID,
  p_quantity DECIMAL,
  p_date DATE DEFAULT CURRENT_DATE
)
RETURNS DECIMAL AS $$
DECLARE
  v_price DECIMAL;
  v_item RECORD;
BEGIN
  -- Buscar precio en pricelist items
  SELECT * INTO v_item
  FROM sales.pricelist_items
  WHERE pricelist_id = p_pricelist_id
    AND product_id = p_product_id
    AND active = TRUE
    AND min_quantity <= p_quantity
    AND (valid_from IS NULL OR valid_from <= p_date)
    AND (valid_to IS NULL OR valid_to >= p_date)
  ORDER BY min_quantity DESC
  LIMIT 1;

  IF FOUND THEN
    RETURN v_item.price;
  END IF;

  -- Buscar por categoria
  SELECT pi.price INTO v_price
  FROM sales.pricelist_items pi
  JOIN inventory.products p ON p.category_id = pi.product_category_id
  WHERE pi.pricelist_id = p_pricelist_id
    AND p.id = p_product_id
    AND pi.active = TRUE
    AND pi.min_quantity <= p_quantity
    AND (pi.valid_from IS NULL OR pi.valid_from <= p_date)
    AND (pi.valid_to IS NULL OR pi.valid_to >= p_date)
  ORDER BY pi.min_quantity DESC
  LIMIT 1;

  IF v_price IS NOT NULL THEN
    RETURN v_price;
  END IF;

  -- Retornar precio del producto
  SELECT list_price INTO v_price
  FROM inventory.products
  WHERE id = p_product_id;

  RETURN COALESCE(v_price, 0);
END;
$$ LANGUAGE plpgsql STABLE;

COMMENT ON FUNCTION sales.get_pricelist_price IS 'COR-050: Get product price from pricelist';

-- =====================================================
-- FIN DEL SCHEMA SALES
-- =====================================================
