-- /home/Autopartes/pos/migrations/v1.0_initial.sql
-- Tenant DB schema v1.0 — 21 tables
-- Source: design spec section 10 (tenant_{id} DB)

-- =====================
-- SUCURSALES
-- =====================
CREATE TABLE branches (
    id              SERIAL PRIMARY KEY,
    name            VARCHAR(200) NOT NULL,
    address         TEXT,
    phone           VARCHAR(20),
    is_active       BOOLEAN DEFAULT TRUE
);

-- =====================
-- EMPLEADOS Y PERMISOS
-- =====================
CREATE TABLE employees (
    id              SERIAL PRIMARY KEY,
    name            VARCHAR(200) NOT NULL,
    email           VARCHAR(200),
    phone           VARCHAR(20),
    pin             VARCHAR(100),               -- hashed, 4 digitos
    password_hash   VARCHAR(200),
    role            VARCHAR(20) NOT NULL,        -- owner, admin, cashier, warehouse, accountant
    branch_id       INTEGER REFERENCES branches(id),
    max_discount_pct NUMERIC(5,2) DEFAULT 0,
    is_active       BOOLEAN DEFAULT TRUE,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE employee_permissions (
    employee_id     INTEGER REFERENCES employees(id),
    permission      VARCHAR(100) NOT NULL,       -- 'pos.sell', 'inventory.adjust', etc.
    PRIMARY KEY (employee_id, permission)
);

CREATE TABLE employee_sessions (
    id              SERIAL PRIMARY KEY,
    employee_id     INTEGER REFERENCES employees(id),
    device_id       VARCHAR(200),
    token           VARCHAR(500) NOT NULL,
    expires_at      TIMESTAMPTZ NOT NULL
);

-- =====================
-- CLIENTES
-- =====================
CREATE TABLE customers (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    name            VARCHAR(300) NOT NULL,
    rfc             VARCHAR(13),
    razon_social    VARCHAR(300),
    regimen_fiscal  VARCHAR(10),                 -- codigo SAT regimen
    uso_cfdi        VARCHAR(10) DEFAULT 'G03',   -- codigo SAT uso CFDI
    cp              VARCHAR(5),
    email           VARCHAR(200),
    phone           VARCHAR(20),
    address         TEXT,
    price_tier      SMALLINT DEFAULT 1 CHECK (price_tier IN (1,2,3)),  -- 1=mostrador, 2=taller, 3=mayoreo
    credit_limit    NUMERIC(12,2) DEFAULT 0,
    credit_balance  NUMERIC(12,2) DEFAULT 0,     -- saldo actual de credito
    is_active       BOOLEAN DEFAULT TRUE,
    vehicle_info    JSONB,                       -- [{make, model, year, vin, plates}]
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

-- =====================
-- INVENTARIO
-- =====================
CREATE TABLE inventory (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    part_number     VARCHAR(100) NOT NULL,
    barcode         VARCHAR(100),
    name            VARCHAR(300) NOT NULL,
    description     TEXT,
    category_id     INTEGER,
    brand           VARCHAR(100),
    vehicle_compatibility JSONB,
    unit            VARCHAR(20) DEFAULT 'PZA',
    cost            NUMERIC(12,2) DEFAULT 0,
    price_1         NUMERIC(12,2) DEFAULT 0,     -- mostrador
    price_2         NUMERIC(12,2) DEFAULT 0,     -- taller
    price_3         NUMERIC(12,2) DEFAULT 0,     -- mayoreo
    tax_rate        NUMERIC(5,4) DEFAULT 0.16,
    min_stock       INTEGER DEFAULT 0,
    max_stock       INTEGER DEFAULT 0,
    location        VARCHAR(50),                 -- ubicacion en almacen
    image_url       VARCHAR(500),
    is_active       BOOLEAN DEFAULT TRUE,
    catalog_part_id INTEGER,                     -- referencia a catalogo Nexus (via API, no FK)
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE inventory_operations (
    id              SERIAL PRIMARY KEY,
    inventory_id    INTEGER REFERENCES inventory(id),
    branch_id       INTEGER REFERENCES branches(id),
    operation_type  VARCHAR(20) NOT NULL,         -- SALE, PURCHASE, RETURN, ADJUST, TRANSFER, INITIAL
    quantity         INTEGER NOT NULL,             -- positivo o negativo
    reference_id    INTEGER,
    reference_type  VARCHAR(50),                  -- 'sale', 'purchase', 'return', etc.
    cost_at_time    NUMERIC(12,2),
    employee_id     INTEGER REFERENCES employees(id),
    device_id       VARCHAR(200),
    notes           TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);
-- Stock actual = SUM(inventory_operations.quantity) WHERE inventory_id=X AND branch_id=Y

-- =====================
-- VENTAS
-- =====================
CREATE TABLE sales (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    customer_id     INTEGER REFERENCES customers(id),  -- NULL = publico general
    employee_id     INTEGER REFERENCES employees(id),
    register_id     INTEGER,                     -- FK cash_registers (deferred, table below)
    sale_type       VARCHAR(20) NOT NULL,         -- cash, credit, mixed
    payment_method  VARCHAR(20),                  -- efectivo, transferencia, tarjeta, mixto
    subtotal        NUMERIC(12,2) NOT NULL,
    discount_total  NUMERIC(12,2) DEFAULT 0,
    tax_total       NUMERIC(12,2) NOT NULL,
    total           NUMERIC(12,2) NOT NULL,
    amount_paid     NUMERIC(12,2) DEFAULT 0,
    change_given    NUMERIC(12,2) DEFAULT 0,
    metodo_pago_sat VARCHAR(3),                   -- PUE o PPD
    forma_pago_sat  VARCHAR(2),                   -- 01, 03, 04, 99
    status          VARCHAR(20) DEFAULT 'completed', -- completed, cancelled, returned
    device_id       VARCHAR(200),
    notes           TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE sale_items (
    id              SERIAL PRIMARY KEY,
    sale_id         INTEGER REFERENCES sales(id),
    inventory_id    INTEGER REFERENCES inventory(id),
    part_number     VARCHAR(100),
    name            VARCHAR(300),
    quantity        INTEGER NOT NULL,
    unit_price      NUMERIC(12,2) NOT NULL,       -- precio al momento de la venta
    unit_cost       NUMERIC(12,2),                -- costo al momento de la venta
    discount_pct    NUMERIC(5,2) DEFAULT 0,
    discount_amount NUMERIC(12,2) DEFAULT 0,
    tax_rate        NUMERIC(5,4) DEFAULT 0.16,
    tax_amount      NUMERIC(12,2) DEFAULT 0,
    subtotal        NUMERIC(12,2) NOT NULL,
    clave_prod_serv VARCHAR(10),                  -- clave SAT
    clave_unidad    VARCHAR(10)                   -- clave unidad SAT
);

-- =====================
-- COTIZACIONES
-- =====================
CREATE TABLE quotations (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    customer_id     INTEGER REFERENCES customers(id),
    employee_id     INTEGER REFERENCES employees(id),
    subtotal        NUMERIC(12,2) NOT NULL,
    tax_total       NUMERIC(12,2) NOT NULL,
    total           NUMERIC(12,2) NOT NULL,
    status          VARCHAR(20) DEFAULT 'active',  -- active, converted, expired, cancelled
    valid_until     DATE,
    converted_sale_id INTEGER REFERENCES sales(id),
    notes           TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE quotation_items (
    id              SERIAL PRIMARY KEY,
    quotation_id    INTEGER REFERENCES quotations(id),
    inventory_id    INTEGER REFERENCES inventory(id),
    part_number     VARCHAR(100),
    name            VARCHAR(300),
    quantity        INTEGER NOT NULL,
    unit_price      NUMERIC(12,2) NOT NULL,
    discount_pct    NUMERIC(5,2) DEFAULT 0,
    tax_rate        NUMERIC(5,4) DEFAULT 0.16,
    subtotal        NUMERIC(12,2) NOT NULL
);

-- =====================
-- APARTADOS (LAYAWAYS)
-- =====================
CREATE TABLE layaways (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    customer_id     INTEGER REFERENCES customers(id) NOT NULL,
    employee_id     INTEGER REFERENCES employees(id),
    total           NUMERIC(12,2) NOT NULL,
    amount_paid     NUMERIC(12,2) DEFAULT 0,
    status          VARCHAR(20) DEFAULT 'active',  -- active, completed, cancelled
    expires_at      DATE,
    converted_sale_id INTEGER REFERENCES sales(id),
    notes           TEXT,
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE layaway_payments (
    id              SERIAL PRIMARY KEY,
    layaway_id      INTEGER REFERENCES layaways(id),
    amount          NUMERIC(12,2) NOT NULL,
    payment_method  VARCHAR(20),
    reference       VARCHAR(100),
    employee_id     INTEGER REFERENCES employees(id),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

-- =====================
-- CAJA REGISTRADORA
-- =====================
CREATE TABLE cash_registers (
    id              SERIAL PRIMARY KEY,
    branch_id       INTEGER REFERENCES branches(id),
    employee_id     INTEGER REFERENCES employees(id),
    register_number SMALLINT NOT NULL,            -- numero de caja (1, 2, 3...)
    opening_amount  NUMERIC(12,2) NOT NULL,       -- fondo inicial
    closing_amount  NUMERIC(12,2),                -- monto contado al cerrar
    expected_amount NUMERIC(12,2),                -- monto esperado calculado
    difference      NUMERIC(12,2),                -- closing - expected
    status          VARCHAR(10) DEFAULT 'open',   -- open, closed
    opened_at       TIMESTAMPTZ DEFAULT NOW(),
    closed_at       TIMESTAMPTZ
);

CREATE TABLE cash_movements (
    id              SERIAL PRIMARY KEY,
    register_id     INTEGER REFERENCES cash_registers(id),
    type            VARCHAR(5) NOT NULL,           -- 'in' o 'out'
    amount          NUMERIC(12,2) NOT NULL,
    reason          VARCHAR(300) NOT NULL,
    employee_id     INTEGER REFERENCES employees(id),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

-- =====================
-- FACTURACION (CFDI QUEUE)
-- =====================
CREATE TABLE cfdi_queue (
    id              SERIAL PRIMARY KEY,
    sale_id         INTEGER REFERENCES sales(id),
    type            VARCHAR(10) NOT NULL,          -- ingreso, egreso, pago
    xml_unsigned    TEXT,                          -- XML generado por POS backend
    xml_signed      TEXT,                          -- XML firmado+timbrado por Horux
    uuid_fiscal     VARCHAR(36),                   -- UUID del SAT
    status          VARCHAR(20) DEFAULT 'pending', -- pending, sending, stamped, failed, cancelled
    retry_count     SMALLINT DEFAULT 0,
    provisional_folio VARCHAR(20),                 -- PRE-XXXXX
    error_message   TEXT,
    cancel_motive   VARCHAR(2),                    -- 01, 02, 03, 04
    cancel_replacement_uuid VARCHAR(36),           -- UUID del CFDI sustituto (motivo 01)
    created_at      TIMESTAMPTZ DEFAULT NOW(),
    stamped_at      TIMESTAMPTZ
);

-- =====================
-- CONTABILIDAD
-- =====================
CREATE TABLE accounts (
    id              SERIAL PRIMARY KEY,
    code            VARCHAR(20) NOT NULL UNIQUE,
    name            VARCHAR(200) NOT NULL,
    parent_id       INTEGER REFERENCES accounts(id),
    type            VARCHAR(20) NOT NULL,          -- activo, pasivo, capital, ingreso, costo, gasto
    sat_code        VARCHAR(20),
    is_system       BOOLEAN DEFAULT FALSE,         -- cuentas predeterminadas no editables
    is_active       BOOLEAN DEFAULT TRUE
);

CREATE TABLE journal_entries (
    id              SERIAL PRIMARY KEY,
    entry_number    INTEGER NOT NULL,
    date            DATE NOT NULL,
    type            VARCHAR(20),                   -- ingreso, egreso, diario, poliza
    description     TEXT,
    reference_type  VARCHAR(50),                   -- sale, purchase, cash_register, etc.
    reference_id    INTEGER,
    status          VARCHAR(20) DEFAULT 'posted',  -- draft, posted, cancelled
    created_by      INTEGER REFERENCES employees(id),
    is_auto         BOOLEAN DEFAULT TRUE,          -- generada automaticamente
    created_at      TIMESTAMPTZ DEFAULT NOW()
);

CREATE TABLE journal_entry_lines (
    id              SERIAL PRIMARY KEY,
    journal_entry_id INTEGER REFERENCES journal_entries(id),
    account_id      INTEGER REFERENCES accounts(id),
    debit           NUMERIC(14,2) DEFAULT 0,
    credit          NUMERIC(14,2) DEFAULT 0,
    description     TEXT
);

CREATE TABLE fiscal_periods (
    id              SERIAL PRIMARY KEY,
    year            SMALLINT NOT NULL,
    month           SMALLINT NOT NULL,
    status          VARCHAR(10) DEFAULT 'open',    -- open, closed
    closed_by       INTEGER REFERENCES employees(id),
    closed_at       TIMESTAMPTZ,
    UNIQUE (year, month)
);

-- =====================
-- AUDITORIA
-- =====================
CREATE TABLE audit_log (
    id              SERIAL PRIMARY KEY,
    employee_id     INTEGER REFERENCES employees(id),
    action          VARCHAR(50) NOT NULL,
    entity_type     VARCHAR(50),
    entity_id       INTEGER,
    old_value       JSONB,
    new_value       JSONB,
    device_id       VARCHAR(200),
    ip_address      VARCHAR(45),
    branch_id       INTEGER REFERENCES branches(id),
    created_at      TIMESTAMPTZ DEFAULT NOW()
);
-- INSERT-only: nunca UPDATE, nunca DELETE

-- =====================
-- INDEXES
-- =====================
CREATE INDEX idx_inv_ops_inventory ON inventory_operations(inventory_id);
CREATE INDEX idx_inv_ops_branch ON inventory_operations(branch_id);
CREATE INDEX idx_inv_ops_type ON inventory_operations(operation_type);
CREATE INDEX idx_inv_ops_created ON inventory_operations(created_at);
CREATE INDEX idx_sales_branch ON sales(branch_id);
CREATE INDEX idx_sales_customer ON sales(customer_id);
CREATE INDEX idx_sales_created ON sales(created_at);
CREATE INDEX idx_sales_status ON sales(status);
CREATE INDEX idx_sale_items_sale ON sale_items(sale_id);
CREATE INDEX idx_inventory_part ON inventory(part_number);
CREATE INDEX idx_inventory_barcode ON inventory(barcode);
CREATE INDEX idx_inventory_branch ON inventory(branch_id);
CREATE INDEX idx_customers_rfc ON customers(rfc);
CREATE INDEX idx_customers_name ON customers(name);
CREATE INDEX idx_cfdi_queue_status ON cfdi_queue(status);
CREATE INDEX idx_cfdi_queue_sale ON cfdi_queue(sale_id);
CREATE INDEX idx_journal_entries_date ON journal_entries(date);
CREATE INDEX idx_journal_lines_entry ON journal_entry_lines(journal_entry_id);
CREATE INDEX idx_audit_log_action ON audit_log(action);
CREATE INDEX idx_audit_log_entity ON audit_log(entity_type, entity_id);
CREATE INDEX idx_audit_log_employee ON audit_log(employee_id);
CREATE INDEX idx_audit_log_created ON audit_log(created_at);
CREATE UNIQUE INDEX idx_inventory_branch_part ON inventory(branch_id, part_number);
CREATE INDEX idx_employee_sessions_token ON employee_sessions(token);

-- =====================
-- PHYSICAL COUNTS (two-phase inventory count)
-- =====================
CREATE TABLE IF NOT EXISTS physical_counts (
    id SERIAL PRIMARY KEY,
    branch_id INTEGER NOT NULL REFERENCES branches(id),
    status VARCHAR(20) NOT NULL DEFAULT 'draft',  -- draft, approved, cancelled
    notes TEXT,
    employee_id INTEGER REFERENCES employees(id),
    approved_by INTEGER REFERENCES employees(id),
    created_at TIMESTAMP DEFAULT NOW(),
    approved_at TIMESTAMP
);

CREATE TABLE IF NOT EXISTS physical_count_lines (
    id SERIAL PRIMARY KEY,
    physical_count_id INTEGER NOT NULL REFERENCES physical_counts(id),
    inventory_id INTEGER NOT NULL REFERENCES inventory(id),
    expected_quantity INTEGER NOT NULL,
    counted_quantity INTEGER NOT NULL,
    difference INTEGER NOT NULL
);

-- =====================
-- TENANT CONFIGURATION (key-value store for CFDI, Horux, etc.)
-- =====================
CREATE TABLE IF NOT EXISTS tenant_config (
    key             VARCHAR(100) PRIMARY KEY,
    value           TEXT,
    updated_at      TIMESTAMPTZ DEFAULT NOW()
);

-- Barcode sequence
CREATE SEQUENCE IF NOT EXISTS barcode_seq START 1;

