# ET-SALES-BACKEND
# Especificacion Tecnica Backend - Modulo Ventas

---

## METADATOS

| Campo | Valor |
|-------|-------|
| **Modulo** | MGN-011 |
| **Version** | 1.0.0 |
| **Fecha** | 2026-01-10 |
| **Estado** | Documentado |
| **Autor** | Claude Code |

---

## 1. SERVICIOS

### 1.1 CustomerGroupsService

**Ubicacion:** `backend/src/modules/sales/customer-groups.service.ts`

| Metodo | Parametros | Retorno | Descripcion |
|--------|------------|---------|-------------|
| `findAll` | `tenantId: string`, `filters: CustomerGroupFilters` | `Promise<{ data: CustomerGroup[]; total: number }>` | Lista grupos de clientes con paginacion y busqueda |
| `findById` | `id: string`, `tenantId: string` | `Promise<CustomerGroup>` | Obtiene grupo por ID con sus miembros |
| `create` | `dto: CreateCustomerGroupDto`, `tenantId: string`, `userId: string` | `Promise<CustomerGroup>` | Crea nuevo grupo de clientes |
| `update` | `id: string`, `dto: UpdateCustomerGroupDto`, `tenantId: string` | `Promise<CustomerGroup>` | Actualiza grupo existente |
| `delete` | `id: string`, `tenantId: string` | `Promise<void>` | Elimina grupo (solo si no tiene miembros) |
| `addMember` | `groupId: string`, `partnerId: string`, `tenantId: string` | `Promise<CustomerGroupMember>` | Agrega cliente al grupo |
| `removeMember` | `groupId: string`, `memberId: string`, `tenantId: string` | `Promise<void>` | Elimina cliente del grupo |

---

### 1.2 SalesTeamsService

**Ubicacion:** `backend/src/modules/sales/sales-teams.service.ts`

| Metodo | Parametros | Retorno | Descripcion |
|--------|------------|---------|-------------|
| `findAll` | `tenantId: string`, `filters: SalesTeamFilters` | `Promise<{ data: SalesTeam[]; total: number }>` | Lista equipos de ventas con filtros |
| `findById` | `id: string`, `tenantId: string` | `Promise<SalesTeam>` | Obtiene equipo por ID con miembros |
| `create` | `dto: CreateSalesTeamDto`, `tenantId: string`, `userId: string` | `Promise<SalesTeam>` | Crea nuevo equipo de ventas |
| `update` | `id: string`, `dto: UpdateSalesTeamDto`, `tenantId: string`, `userId: string` | `Promise<SalesTeam>` | Actualiza equipo existente |
| `addMember` | `teamId: string`, `userId: string`, `role: string`, `tenantId: string` | `Promise<SalesTeamMember>` | Agrega usuario al equipo |
| `removeMember` | `teamId: string`, `memberId: string`, `tenantId: string` | `Promise<void>` | Elimina usuario del equipo |

---

### 1.3 PricelistsService

**Ubicacion:** `backend/src/modules/sales/pricelists.service.ts`

| Metodo | Parametros | Retorno | Descripcion |
|--------|------------|---------|-------------|
| `findAll` | `tenantId: string`, `filters: PricelistFilters` | `Promise<{ data: Pricelist[]; total: number }>` | Lista listas de precios |
| `findById` | `id: string`, `tenantId: string` | `Promise<Pricelist>` | Obtiene lista de precios con items |
| `create` | `dto: CreatePricelistDto`, `tenantId: string`, `userId: string` | `Promise<Pricelist>` | Crea nueva lista de precios |
| `update` | `id: string`, `dto: UpdatePricelistDto`, `tenantId: string`, `userId: string` | `Promise<Pricelist>` | Actualiza lista de precios |
| `addItem` | `pricelistId: string`, `dto: CreatePricelistItemDto`, `tenantId: string`, `userId: string` | `Promise<PricelistItem>` | Agrega item a lista de precios |
| `removeItem` | `pricelistId: string`, `itemId: string`, `tenantId: string` | `Promise<void>` | Elimina item de lista de precios |
| `getProductPrice` | `productId: string`, `pricelistId: string`, `quantity: number` | `Promise<number \| null>` | Obtiene precio de producto segun lista y cantidad |

---

### 1.4 OrdersService

**Ubicacion:** `backend/src/modules/sales/orders.service.ts`

| Metodo | Parametros | Retorno | Descripcion |
|--------|------------|---------|-------------|
| `findAll` | `tenantId: string`, `filters: SalesOrderFilters` | `Promise<{ data: SalesOrder[]; total: number }>` | Lista ordenes de venta con filtros avanzados |
| `findById` | `id: string`, `tenantId: string` | `Promise<SalesOrder>` | Obtiene orden por ID con lineas |
| `create` | `dto: CreateSalesOrderDto`, `tenantId: string`, `userId: string` | `Promise<SalesOrder>` | Crea nueva orden de venta |
| `update` | `id: string`, `dto: UpdateSalesOrderDto`, `tenantId: string`, `userId: string` | `Promise<SalesOrder>` | Actualiza orden (solo en estado draft) |
| `delete` | `id: string`, `tenantId: string` | `Promise<void>` | Elimina orden (solo en estado draft) |
| `addLine` | `orderId: string`, `dto: CreateSalesOrderLineDto`, `tenantId: string`, `userId: string` | `Promise<SalesOrderLine>` | Agrega linea a orden |
| `updateLine` | `orderId: string`, `lineId: string`, `dto: UpdateSalesOrderLineDto`, `tenantId: string` | `Promise<SalesOrderLine>` | Actualiza linea de orden |
| `removeLine` | `orderId: string`, `lineId: string`, `tenantId: string` | `Promise<void>` | Elimina linea de orden |
| `confirm` | `id: string`, `tenantId: string`, `userId: string` | `Promise<SalesOrder>` | Confirma orden (draft -> sent) |
| `cancel` | `id: string`, `tenantId: string`, `userId: string` | `Promise<SalesOrder>` | Cancela orden |
| `createInvoice` | `id: string`, `tenantId: string`, `userId: string` | `Promise<{ orderId: string; invoiceId: string }>` | Genera factura desde orden |

---

### 1.5 QuotationsService

**Ubicacion:** `backend/src/modules/sales/quotations.service.ts`

| Metodo | Parametros | Retorno | Descripcion |
|--------|------------|---------|-------------|
| `findAll` | `tenantId: string`, `filters: QuotationFilters` | `Promise<{ data: Quotation[]; total: number }>` | Lista cotizaciones con filtros |
| `findById` | `id: string`, `tenantId: string` | `Promise<Quotation>` | Obtiene cotizacion por ID con lineas |
| `create` | `dto: CreateQuotationDto`, `tenantId: string`, `userId: string` | `Promise<Quotation>` | Crea nueva cotizacion |
| `update` | `id: string`, `dto: UpdateQuotationDto`, `tenantId: string`, `userId: string` | `Promise<Quotation>` | Actualiza cotizacion (solo en draft) |
| `delete` | `id: string`, `tenantId: string` | `Promise<void>` | Elimina cotizacion (solo en draft) |
| `addLine` | `quotationId: string`, `dto: CreateQuotationLineDto`, `tenantId: string`, `userId: string` | `Promise<QuotationLine>` | Agrega linea a cotizacion |
| `updateLine` | `quotationId: string`, `lineId: string`, `dto: UpdateQuotationLineDto`, `tenantId: string` | `Promise<QuotationLine>` | Actualiza linea de cotizacion |
| `removeLine` | `quotationId: string`, `lineId: string`, `tenantId: string` | `Promise<void>` | Elimina linea de cotizacion |
| `send` | `id: string`, `tenantId: string`, `userId: string` | `Promise<Quotation>` | Envia cotizacion al cliente (email) |
| `confirm` | `id: string`, `tenantId: string`, `userId: string` | `Promise<{ quotation: Quotation; orderId: string }>` | Confirma cotizacion y crea orden de venta |
| `cancel` | `id: string`, `tenantId: string`, `userId: string` | `Promise<Quotation>` | Cancela cotizacion |

---

## 2. ENTIDADES

### 2.1 CustomerGroup

**Schema:** `sales.customer_groups`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `tenant_id` | UUID | NO | Tenant (multi-tenancy) |
| `name` | VARCHAR(255) | NO | Nombre del grupo |
| `description` | TEXT | SI | Descripcion |
| `discount_percentage` | DECIMAL | NO | Porcentaje de descuento (default: 0) |
| `created_by` | UUID | SI | Usuario que creo |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |

### 2.2 CustomerGroupMember

**Schema:** `sales.customer_group_members`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `customer_group_id` | UUID | NO | FK a customer_groups |
| `partner_id` | UUID | NO | FK a core.partners |
| `joined_at` | TIMESTAMP | NO | Fecha de union |

### 2.3 SalesTeam

**Schema:** `sales.sales_teams`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `tenant_id` | UUID | NO | Tenant |
| `company_id` | UUID | NO | FK a auth.companies |
| `name` | VARCHAR(255) | NO | Nombre del equipo |
| `code` | VARCHAR(50) | SI | Codigo unico por empresa |
| `team_leader_id` | UUID | SI | FK a auth.users (lider) |
| `target_monthly` | DECIMAL | SI | Meta mensual |
| `target_annual` | DECIMAL | SI | Meta anual |
| `active` | BOOLEAN | NO | Estado activo (default: true) |
| `created_by` | UUID | SI | Usuario que creo |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |
| `updated_by` | UUID | SI | Usuario que actualizo |
| `updated_at` | TIMESTAMP | SI | Fecha de actualizacion |

### 2.4 SalesTeamMember

**Schema:** `sales.sales_team_members`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `sales_team_id` | UUID | NO | FK a sales_teams |
| `user_id` | UUID | NO | FK a auth.users |
| `role` | VARCHAR(100) | SI | Rol en el equipo |
| `joined_at` | TIMESTAMP | NO | Fecha de union |

### 2.5 Pricelist

**Schema:** `sales.pricelists`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `tenant_id` | UUID | NO | Tenant |
| `company_id` | UUID | SI | FK a auth.companies |
| `name` | VARCHAR(255) | NO | Nombre de la lista |
| `currency_id` | UUID | NO | FK a core.currencies |
| `active` | BOOLEAN | NO | Estado activo (default: true) |
| `created_by` | UUID | SI | Usuario que creo |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |
| `updated_by` | UUID | SI | Usuario que actualizo |
| `updated_at` | TIMESTAMP | SI | Fecha de actualizacion |

### 2.6 PricelistItem

**Schema:** `sales.pricelist_items`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `pricelist_id` | UUID | NO | FK a pricelists |
| `product_id` | UUID | SI | FK a inventory.products |
| `product_category_id` | UUID | SI | FK a core.product_categories |
| `price` | DECIMAL | NO | Precio |
| `min_quantity` | INTEGER | NO | Cantidad minima (default: 1) |
| `valid_from` | DATE | SI | Fecha inicio validez |
| `valid_to` | DATE | SI | Fecha fin validez |
| `active` | BOOLEAN | NO | Estado activo (default: true) |
| `created_by` | UUID | SI | Usuario que creo |

### 2.7 SalesOrder

**Schema:** `sales.sales_orders`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `tenant_id` | UUID | NO | Tenant |
| `company_id` | UUID | NO | FK a auth.companies |
| `name` | VARCHAR(50) | NO | Numero de orden (SO-XXXXXX) |
| `client_order_ref` | VARCHAR(100) | SI | Referencia del cliente |
| `partner_id` | UUID | NO | FK a core.partners |
| `order_date` | DATE | NO | Fecha de orden |
| `validity_date` | DATE | SI | Fecha de validez |
| `commitment_date` | DATE | SI | Fecha de compromiso |
| `currency_id` | UUID | NO | FK a core.currencies |
| `pricelist_id` | UUID | SI | FK a pricelists |
| `payment_term_id` | UUID | SI | FK a financial.payment_terms |
| `user_id` | UUID | SI | FK a auth.users (vendedor) |
| `sales_team_id` | UUID | SI | FK a sales_teams |
| `amount_untaxed` | DECIMAL | NO | Subtotal sin impuestos |
| `amount_tax` | DECIMAL | NO | Total impuestos |
| `amount_total` | DECIMAL | NO | Total |
| `status` | ENUM | NO | draft, sent, sale, done, cancelled |
| `invoice_status` | ENUM | NO | pending, partial, invoiced |
| `delivery_status` | ENUM | NO | pending, partial, delivered |
| `invoice_policy` | ENUM | NO | order, delivery |
| `picking_id` | UUID | SI | FK a inventory.pickings |
| `notes` | TEXT | SI | Notas internas |
| `terms_conditions` | TEXT | SI | Terminos y condiciones |
| `created_by` | UUID | SI | Usuario que creo |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |
| `confirmed_at` | TIMESTAMP | SI | Fecha de confirmacion |
| `confirmed_by` | UUID | SI | Usuario que confirmo |
| `cancelled_at` | TIMESTAMP | SI | Fecha de cancelacion |
| `cancelled_by` | UUID | SI | Usuario que cancelo |
| `updated_by` | UUID | SI | Usuario que actualizo |
| `updated_at` | TIMESTAMP | SI | Fecha de actualizacion |

### 2.8 SalesOrderLine

**Schema:** `sales.sales_order_lines`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `order_id` | UUID | NO | FK a sales_orders |
| `tenant_id` | UUID | NO | Tenant |
| `product_id` | UUID | NO | FK a inventory.products |
| `description` | TEXT | NO | Descripcion |
| `quantity` | DECIMAL | NO | Cantidad |
| `qty_delivered` | DECIMAL | NO | Cantidad entregada |
| `qty_invoiced` | DECIMAL | NO | Cantidad facturada |
| `uom_id` | UUID | NO | FK a core.uom |
| `price_unit` | DECIMAL | NO | Precio unitario |
| `discount` | DECIMAL | NO | Porcentaje descuento |
| `tax_ids` | UUID[] | NO | Array de FK a financial.taxes |
| `amount_untaxed` | DECIMAL | NO | Subtotal sin impuestos |
| `amount_tax` | DECIMAL | NO | Total impuestos |
| `amount_total` | DECIMAL | NO | Total |
| `analytic_account_id` | UUID | SI | FK a financial.analytic_accounts |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |
| `updated_at` | TIMESTAMP | SI | Fecha de actualizacion |

### 2.9 Quotation

**Schema:** `sales.quotations`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `tenant_id` | UUID | NO | Tenant |
| `company_id` | UUID | NO | FK a auth.companies |
| `name` | VARCHAR(50) | NO | Numero de cotizacion (QUO-XXXXXX) |
| `partner_id` | UUID | NO | FK a core.partners |
| `quotation_date` | DATE | NO | Fecha de cotizacion |
| `validity_date` | DATE | NO | Fecha de validez |
| `currency_id` | UUID | NO | FK a core.currencies |
| `pricelist_id` | UUID | SI | FK a pricelists |
| `user_id` | UUID | SI | FK a auth.users (vendedor) |
| `sales_team_id` | UUID | SI | FK a sales_teams |
| `amount_untaxed` | DECIMAL | NO | Subtotal sin impuestos |
| `amount_tax` | DECIMAL | NO | Total impuestos |
| `amount_total` | DECIMAL | NO | Total |
| `status` | ENUM | NO | draft, sent, confirmed, cancelled, expired |
| `sale_order_id` | UUID | SI | FK a sales_orders (orden generada) |
| `notes` | TEXT | SI | Notas internas |
| `terms_conditions` | TEXT | SI | Terminos y condiciones |
| `created_by` | UUID | SI | Usuario que creo |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |
| `updated_by` | UUID | SI | Usuario que actualizo |
| `updated_at` | TIMESTAMP | SI | Fecha de actualizacion |

### 2.10 QuotationLine

**Schema:** `sales.quotation_lines`

| Columna | Tipo | Nullable | Descripcion |
|---------|------|----------|-------------|
| `id` | UUID | NO | Identificador unico |
| `quotation_id` | UUID | NO | FK a quotations |
| `tenant_id` | UUID | NO | Tenant |
| `product_id` | UUID | SI | FK a inventory.products |
| `description` | TEXT | NO | Descripcion |
| `quantity` | DECIMAL | NO | Cantidad |
| `uom_id` | UUID | NO | FK a core.uom |
| `price_unit` | DECIMAL | NO | Precio unitario |
| `discount` | DECIMAL | NO | Porcentaje descuento |
| `tax_ids` | UUID[] | NO | Array de FK a financial.taxes |
| `amount_untaxed` | DECIMAL | NO | Subtotal sin impuestos |
| `amount_tax` | DECIMAL | NO | Total impuestos |
| `amount_total` | DECIMAL | NO | Total |
| `created_at` | TIMESTAMP | NO | Fecha de creacion |

---

## 3. DTOs

### 3.1 CustomerGroups DTOs

```typescript
interface CreateCustomerGroupDto {
  name: string;                    // Requerido, max 255
  description?: string;            // Opcional
  discount_percentage?: number;    // 0-100, default: 0
}

interface UpdateCustomerGroupDto {
  name?: string;                   // max 255
  description?: string | null;
  discount_percentage?: number;    // 0-100
}

interface CustomerGroupFilters {
  search?: string;
  page?: number;                   // default: 1
  limit?: number;                  // default: 20, max: 100
}
```

### 3.2 SalesTeams DTOs

```typescript
interface CreateSalesTeamDto {
  company_id: string;              // UUID, requerido
  name: string;                    // Requerido, max 255
  code?: string;                   // max 50
  team_leader_id?: string;         // UUID
  target_monthly?: number;         // > 0
  target_annual?: number;          // > 0
}

interface UpdateSalesTeamDto {
  name?: string;
  code?: string;
  team_leader_id?: string | null;
  target_monthly?: number | null;
  target_annual?: number | null;
  active?: boolean;
}

interface SalesTeamFilters {
  company_id?: string;
  active?: boolean;
  page?: number;
  limit?: number;
}
```

### 3.3 Pricelists DTOs

```typescript
interface CreatePricelistDto {
  company_id?: string;             // UUID
  name: string;                    // Requerido, max 255
  currency_id: string;             // UUID, requerido
}

interface UpdatePricelistDto {
  name?: string;
  currency_id?: string;
  active?: boolean;
}

interface CreatePricelistItemDto {
  product_id?: string;             // UUID (uno de product_id o product_category_id)
  product_category_id?: string;    // UUID
  price: number;                   // >= 0
  min_quantity?: number;           // > 0, default: 1
  valid_from?: string;             // ISO date
  valid_to?: string;               // ISO date
}

interface PricelistFilters {
  company_id?: string;
  active?: boolean;
  page?: number;
  limit?: number;
}
```

### 3.4 Orders DTOs

```typescript
interface CreateSalesOrderDto {
  company_id: string;              // UUID, requerido
  partner_id: string;              // UUID, requerido
  client_order_ref?: string;       // max 100
  order_date?: string;             // ISO date
  validity_date?: string;          // ISO date
  commitment_date?: string;        // ISO date
  currency_id: string;             // UUID, requerido
  pricelist_id?: string;           // UUID
  payment_term_id?: string;        // UUID
  sales_team_id?: string;          // UUID
  invoice_policy?: 'order' | 'delivery';  // default: 'order'
  notes?: string;
  terms_conditions?: string;
}

interface UpdateSalesOrderDto {
  partner_id?: string;
  client_order_ref?: string | null;
  order_date?: string;
  validity_date?: string | null;
  commitment_date?: string | null;
  currency_id?: string;
  pricelist_id?: string | null;
  payment_term_id?: string | null;
  sales_team_id?: string | null;
  invoice_policy?: 'order' | 'delivery';
  notes?: string | null;
  terms_conditions?: string | null;
}

interface CreateSalesOrderLineDto {
  product_id: string;              // UUID, requerido
  description: string;             // Requerido
  quantity: number;                // > 0
  uom_id: string;                  // UUID, requerido
  price_unit: number;              // >= 0
  discount?: number;               // 0-100, default: 0
  tax_ids?: string[];              // UUID[]
  analytic_account_id?: string;    // UUID
}

interface UpdateSalesOrderLineDto {
  description?: string;
  quantity?: number;
  uom_id?: string;
  price_unit?: number;
  discount?: number;
  tax_ids?: string[];
  analytic_account_id?: string | null;
}

interface SalesOrderFilters {
  company_id?: string;
  partner_id?: string;
  status?: 'draft' | 'sent' | 'sale' | 'done' | 'cancelled';
  invoice_status?: 'pending' | 'partial' | 'invoiced';
  delivery_status?: 'pending' | 'partial' | 'delivered';
  date_from?: string;
  date_to?: string;
  search?: string;
  page?: number;
  limit?: number;
}
```

### 3.5 Quotations DTOs

```typescript
interface CreateQuotationDto {
  company_id: string;              // UUID, requerido
  partner_id: string;              // UUID, requerido
  quotation_date?: string;         // ISO date
  validity_date: string;           // ISO date, requerido
  currency_id: string;             // UUID, requerido
  pricelist_id?: string;           // UUID
  sales_team_id?: string;          // UUID
  notes?: string;
  terms_conditions?: string;
}

interface UpdateQuotationDto {
  partner_id?: string;
  quotation_date?: string;
  validity_date?: string;
  currency_id?: string;
  pricelist_id?: string | null;
  sales_team_id?: string | null;
  notes?: string | null;
  terms_conditions?: string | null;
}

interface CreateQuotationLineDto {
  product_id?: string;             // UUID
  description: string;             // Requerido
  quantity: number;                // > 0
  uom_id: string;                  // UUID, requerido
  price_unit: number;              // >= 0
  discount?: number;               // 0-100, default: 0
  tax_ids?: string[];              // UUID[]
}

interface UpdateQuotationLineDto {
  description?: string;
  quantity?: number;
  uom_id?: string;
  price_unit?: number;
  discount?: number;
  tax_ids?: string[];
}

interface QuotationFilters {
  company_id?: string;
  partner_id?: string;
  status?: 'draft' | 'sent' | 'confirmed' | 'cancelled' | 'expired';
  date_from?: string;
  date_to?: string;
  search?: string;
  page?: number;
  limit?: number;
}
```

---

## 4. ENDPOINTS

### 4.1 Customer Groups

| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | `/api/sales/customer-groups` | Listar grupos de clientes |
| GET | `/api/sales/customer-groups/:id` | Obtener grupo por ID |
| POST | `/api/sales/customer-groups` | Crear grupo de clientes |
| PATCH | `/api/sales/customer-groups/:id` | Actualizar grupo |
| DELETE | `/api/sales/customer-groups/:id` | Eliminar grupo |
| POST | `/api/sales/customer-groups/:id/members` | Agregar miembro |
| DELETE | `/api/sales/customer-groups/:id/members/:memberId` | Eliminar miembro |

### 4.2 Sales Teams

| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | `/api/sales/teams` | Listar equipos de ventas |
| GET | `/api/sales/teams/:id` | Obtener equipo por ID |
| POST | `/api/sales/teams` | Crear equipo de ventas |
| PATCH | `/api/sales/teams/:id` | Actualizar equipo |
| POST | `/api/sales/teams/:id/members` | Agregar miembro |
| DELETE | `/api/sales/teams/:id/members/:memberId` | Eliminar miembro |

### 4.3 Pricelists

| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | `/api/sales/pricelists` | Listar listas de precios |
| GET | `/api/sales/pricelists/:id` | Obtener lista por ID |
| POST | `/api/sales/pricelists` | Crear lista de precios |
| PATCH | `/api/sales/pricelists/:id` | Actualizar lista |
| POST | `/api/sales/pricelists/:id/items` | Agregar item |
| DELETE | `/api/sales/pricelists/:id/items/:itemId` | Eliminar item |

### 4.4 Sales Orders

| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | `/api/sales/orders` | Listar ordenes de venta |
| GET | `/api/sales/orders/:id` | Obtener orden por ID |
| POST | `/api/sales/orders` | Crear orden de venta |
| PATCH | `/api/sales/orders/:id` | Actualizar orden |
| DELETE | `/api/sales/orders/:id` | Eliminar orden |
| POST | `/api/sales/orders/:id/lines` | Agregar linea |
| PATCH | `/api/sales/orders/:id/lines/:lineId` | Actualizar linea |
| DELETE | `/api/sales/orders/:id/lines/:lineId` | Eliminar linea |
| POST | `/api/sales/orders/:id/confirm` | Confirmar orden |
| POST | `/api/sales/orders/:id/cancel` | Cancelar orden |
| POST | `/api/sales/orders/:id/invoice` | Crear factura |

### 4.5 Quotations

| Metodo | Endpoint | Descripcion |
|--------|----------|-------------|
| GET | `/api/sales/quotations` | Listar cotizaciones |
| GET | `/api/sales/quotations/:id` | Obtener cotizacion por ID |
| POST | `/api/sales/quotations` | Crear cotizacion |
| PATCH | `/api/sales/quotations/:id` | Actualizar cotizacion |
| DELETE | `/api/sales/quotations/:id` | Eliminar cotizacion |
| POST | `/api/sales/quotations/:id/lines` | Agregar linea |
| PATCH | `/api/sales/quotations/:id/lines/:lineId` | Actualizar linea |
| DELETE | `/api/sales/quotations/:id/lines/:lineId` | Eliminar linea |
| POST | `/api/sales/quotations/:id/send` | Enviar cotizacion |
| POST | `/api/sales/quotations/:id/confirm` | Confirmar y crear orden |
| POST | `/api/sales/quotations/:id/cancel` | Cancelar cotizacion |

---

## 5. TESTS

### 5.1 Estado de Tests

| Servicio | Archivo | Estado |
|----------|---------|--------|
| CustomerGroupsService | `__tests__/customer-groups.service.spec.ts` | Implementado |
| SalesTeamsService | `__tests__/sales-teams.service.spec.ts` | Implementado |
| PricelistsService | `__tests__/pricelists.service.spec.ts` | Implementado |
| OrdersService | `__tests__/orders.service.spec.ts` | Implementado |
| QuotationsService | `__tests__/quotations.service.spec.ts` | Implementado |

### 5.2 Cobertura

Todos los servicios cuentan con tests unitarios que cubren:
- CRUD basico
- Validaciones de negocio
- Manejo de errores
- Flujos de estado (confirmar, cancelar, etc.)

---

## 6. DEPENDENCIAS

### 6.1 Modulos Internos

| Modulo | Uso |
|--------|-----|
| `auth` | Usuarios, empresas, autenticacion |
| `core` | Partners, currencies, UoM, product_categories, sequences |
| `inventory` | Products |
| `financial` | Taxes (calculo impuestos), invoices, payment_terms, analytic_accounts |

### 6.2 Servicios Externos

| Servicio | Uso |
|----------|-----|
| `taxesService` | Calculo de impuestos en lineas de orden/cotizacion |
| `sequencesService` | Generacion de numeros de secuencia (SO-XXXXXX) |
| `emailService` | Envio de cotizaciones por email |

### 6.3 Dependencias de Base de Datos

```
sales.customer_groups
  └── core.partners (via customer_group_members)

sales.sales_teams
  ├── auth.companies
  └── auth.users (team_leader, members)

sales.pricelists
  ├── auth.companies
  ├── core.currencies
  ├── inventory.products (items)
  └── core.product_categories (items)

sales.sales_orders
  ├── auth.companies
  ├── core.partners
  ├── core.currencies
  ├── sales.pricelists
  ├── sales.sales_teams
  ├── auth.users
  ├── financial.payment_terms
  ├── inventory.products (lines)
  ├── core.uom (lines)
  └── financial.taxes (lines)

sales.quotations
  ├── (mismas dependencias que sales_orders)
  └── sales.sales_orders (sale_order_id)
```

---

## 7. FLUJOS DE NEGOCIO

### 7.1 Flujo de Cotizacion

```
draft -> sent -> confirmed -> [crea sales_order]
              -> cancelled
              -> expired
```

### 7.2 Flujo de Orden de Venta

```
draft -> sent -> sale -> done
              -> cancelled

invoice_status: pending -> partial -> invoiced
delivery_status: pending -> partial -> delivered
```

### 7.3 Reglas de Negocio

1. **Ordenes/Cotizaciones**: Solo se pueden editar/eliminar en estado `draft`
2. **Grupos de clientes**: No se pueden eliminar si tienen miembros
3. **Equipos de ventas**: Codigo unico por empresa
4. **Listas de precios**: Nombre unico por tenant
5. **Facturacion**: Segun `invoice_policy` (order: al confirmar, delivery: al entregar)
6. **Impuestos**: Calculados automaticamente usando `taxesService`

---

## 8. VALIDACIONES

### 8.1 Validaciones con Zod

Todas las validaciones de entrada se realizan usando Zod en el controlador:
- Tipos de datos
- Campos requeridos
- Rangos (min, max)
- Formatos (UUID, email, fechas)
- Valores permitidos (enums)

### 8.2 Validaciones de Negocio

Las validaciones de negocio se realizan en los servicios:
- Unicidad de nombres/codigos
- Estados validos para operaciones
- Existencia de entidades relacionadas
- Restricciones de eliminacion
