Skip to content

Sales Module

Sales order management, quote builder, and sales dashboard.

Overview

The Sales module provides:

  • Sales order list with state tabs
  • Deep search across all orders
  • Sales dashboard with analytics
  • Quote creation from CRM leads
  • Quote line editing with sections
  • Photo/video gallery with S3 storage
  • Activity logging

Routes

Sales Order List

GET /sales

Lists sales orders with state-based tabs.

Parameter Type Description
state string Filter by state: sale, sent, draft

States displayed:

State Label Description
sale Sales Order Confirmed orders
sent Quotation Sent Sent but not confirmed
draft Quotation Draft quotes

GET /sales/search

Deep search across all sales orders.

Parameter Type Description
q string Search query (order name or customer)

Order Detail

GET /sales/{order_id}

Displays full order details with:

  • Order lines grouped by sections
  • Customer information
  • Activity log
  • Photo/video gallery

Sales Dashboard

GET /sales/dashboard

Analytics dashboard showing:

  • This week's sales (count and total)
  • This month's sales
  • Last month's sales
  • 6-month bar chart

Quote Edit

GET /quotes/{quote_id}/edit

Quote builder interface for editing quotes:

  • Add/edit sections (rooms)
  • Add product lines with dimensions
  • Add notes
  • Upload photos/videos
  • Reorder items via drag-drop

API Endpoints

Quote Management

Create Quote from Lead

GET /crm/lead/{lead_id}/quote/new

Creates a draft quote linked to CRM lead and redirects to edit page.

Add Section

POST /api/quotes/{quote_id}/sections

{
  "name": "Living Room"
}

Response:

{
  "success": true,
  "section_id": 123
}

Add Note

POST /api/quotes/{quote_id}/notes

{
  "note": "Customer prefers morning installation"
}

Add Product Line

POST /api/quotes/{quote_id}/lines

{
  "product_id": 45,
  "width": 48.5,
  "height": 72,
  "quantity": 2,
  "location_id": 12,
  "motorized": "Motorized",
  "mount": "in",
  "valance": "standard",
  "remote": "YES",
  "smart_hub": "NO",
  "price_unit": 299.99
}

Update Line

PUT /api/quotes/{quote_id}/lines/{line_id}

{
  "width": 50,
  "price_unit": 349.99
}

Delete Line

DELETE /api/quotes/{quote_id}/lines/{line_id}

Reorder Lines

POST /api/quotes/{quote_id}/reorder

{
  "lines": [
    {"id": 101, "sequence": 10},
    {"id": 102, "sequence": 20},
    {"id": 103, "sequence": 30}
  ]
}

Duplicate Quote

POST /api/quotes/{quote_id}/duplicate

Response:

{
  "success": true,
  "new_quote_id": 456,
  "redirect_url": "/quotes/456/edit"
}

Duplicate Line

POST /api/quotes/{quote_id}/lines/{line_id}/duplicate

Send Quote Email

POST /api/quotes/{quote_id}/send

Sends quotation email with PDF and portal link.

Response:

{
  "success": true,
  "message_id": 789,
  "template_id": 12
}

Photo/Video Management

Upload Photo

POST /api/quotes/{quote_id}/photos

Multipart form with file field (image/*).

Response:

{
  "success": true,
  "attachment_id": 234,
  "url": "https://s3.amazonaws.com/...",
  "filename": "1702345678_abc12345.jpg",
  "s3_path": "bucket/quotes/123/photos/1702345678_abc12345.jpg"
}

Upload Video

POST /api/quotes/{quote_id}/videos

Multipart form with file field (video/*).

Delete Attachment

DELETE /api/quotes/{quote_id}/attachments/{attachment_id}

Deletes from both S3 and Odoo.

GET /api/products/search

Parameter Type Description
q string Search term (name or code)

Response:

{
  "success": true,
  "products": [
    {"id": 45, "name": "Premium Blinds", "default_code": "BL-001", "list_price": 199.99}
  ]
}

Locations

GET /api/locations

Get all item locations for dropdown.

POST /api/locations

Create new location:

{
  "name": "Master Bedroom"
}

Comments

POST /api/sales/{order_id}/comment

{
  "comment": "Customer requested rush delivery"
}

Dashboard Analytics

Date Ranges

Period Calculation
This Week Monday to today
This Month 1st to today
Last Month 1st to last day of previous month

Sales Stats Query

def get_sales_stats_by_date_range(start_date, end_date):
    domain = [
        ('state', '=', 'sale'),  # Only confirmed orders
        ('date_order', '>=', f'{start_date} 00:00:00'),
        ('date_order', '<=', f'{end_date} 23:59:59')
    ]
    # Returns count and total_amount

Quote Line Types

display_type Description
(none) Product line with pricing
line_section Section header (room/floor)
line_note Note/comment line

Product Line Fields

Field Type Description
product_id Many2one Product reference
width Float Width in inches/mm
height Float Height in inches/mm
product_uom_qty Float Quantity
price_unit Float Unit price
item_location_id Many2one Room/location
motorized Selection L, R, Motorized, Cordless
mount Selection in (Inside), out (Outside)
valance Selection open, standard, compact_25
remote Selection YES, NO
smart_hub Selection YES, NO

S3 Photo Storage

Photos are stored in S3 with path format:

quotes/{order_id}/photos/{timestamp}_{uuid}.jpg

Videos:

quotes/{order_id}/videos/{timestamp}_{uuid}.mp4

Photos are linked to Odoo via ir.attachment with type='url'.

Send Quote Flow

sequenceDiagram
    participant User
    participant PWA
    participant Odoo
    participant Email

    User->>PWA: Click "Send Quote"
    PWA->>Odoo: Find email template
    PWA->>Odoo: message_post_with_template()
    Odoo->>Odoo: Generate PDF
    Odoo->>Email: Send email with PDF
    PWA->>Odoo: Update state to 'sent'
    PWA-->>User: Success

Activity Attribution

Comments are attributed to the logged-in user:

partner_id = session.get('partner_id')
result = odoo_api.post_sale_order_comment(order_id, comment, author_id=partner_id)