API Reference

This document describes the REST API for three construction pricing databases: Batichiffrage, Batiprix, and ProgBat.

Base URLs

https://bp.groupedsd.net      # Full application (frontend + API)
https://api.bp.groupedsd.net  # API-only (recommended for integrations)

Overview

Batichiffrage API (/api/bc_*)

Batichiffrage is a French construction pricing database that provides detailed work items (ouvrages) with their compositions and pricing. It organizes data in a hierarchical family structure and includes detailed component breakdowns (TCompo) for each work item.

Key Features: - Hierarchical category system (familles) - Detailed work item compositions with labor, materials, and pricing - Supplier/manufacturer data integration - Base64-encoded composition data with parsed TCompo arrays

Tables: - bc_ouvrages - Detailed work items with compositions and pricing - bc_familles - Category/family hierarchy for organizing work items - bc_fournisseurs - Supplier/manufacturer information - bc_fournisseurs_familles - Supplier category mappings - bc_fournisseurs_fournitures - Supplier product listings

Batiprix API (/api/bp/*)

Batiprix is another French construction pricing reference that provides standardized pricing for construction work. It uses a nomenclature-based organization system and includes comprehensive pricing tiers for labor, supplies, and installation.

Key Features: - Nomenclature-based categorization (e.g., "01 07 06 03 00 003") - Multiple pricing tiers (cost price, supply+install, selling prices) - Labor time and cost calculations - Support for custom work items

Tables: - bp_categories - Nomenclature hierarchy from HTML data - bp_familles - Parent category hierarchy from JSON responses - bp_ouvrages - Work items with complete pricing data - bp_ouvrages_elements - Component elements within work items - bp_chiffrages_elements - Pricing/quote section elements

ProgBat API (/api/pg/*)

ProgBat is a DSD internal pricing library containing supplies (fournitures), work items (ouvrages), and text templates. Data is imported from Excel files exported from the ProgBat application.

Key Features: - Supply items with purchase price, margin, and selling price - Work items with labor costs (déboursé HT) and margins - Text templates for quotes and documents - Excel import support with French field names

Tables: - pg_fournitures - Supply/material items with pricing - pg_ouvrages - Work items/services with labor costs - pg_textes - Text templates for documents


Batichiffrage API

BC Tables Overview

  1. bc_ouvrages - Detailed work items with compositions and pricing
  2. bc_familles - Category/family hierarchy for organizing work items

Table: bc_ouvrages

Data Model

{
  "id": 207141,
  "id_parent": "69490",
  "type": "expert",
  "code_ouvrage": 207141,
  "libtech": "Prise en charge des frais fixes en milieu rural.",
  "libelle": "Prise en charge frais fixes en milieu rural",
  "libcom": "Prise en charge frais fixes en milieu rural",
  "unite": "U",
  "prix": 47.728,
  "compos": "YToxOntpOjA7YToxNjp7czo0OiJjb21wIjtzOjI6Ik1PIjtzOjQ6ImNmYWIiO3M6NjoiMDAwME1PIjtzOjc6ImxpYmVsbGUiO3M6MTM6Ik1haW4gZCdvZXV2cmUiO3M6NzoibGlidGVjaCI7czoxMzoiTWFpbiBkJ29ldXZyZSI7czo4OiJyZWZmYWJyaSI7czoyOiJNTyI7czo1OiJwcml4dCI7aTowO3M6NDoiY29uZCI7aTowO3M6NzoiaGF1c3JhYiI7aTowO3M6NjoicmVtaXNlIjtpOjA7czo0OiJwcml4IjtpOjA7czo5OiJjb2VmZl9lY28iO3M6MToiMSI7czo5OiJjb2VmZl9zdGQiO3M6MToiMSI7czoxMDoiY29lZmZfbHV4ZSI7czoxOiIxIjtzOjg6InF1YW50aXRlIjtkOjAuODAwMDAwMDAwMDAwMDAwMDQ7czo0OiJ0eXBlIjtzOjE6IjkiO3M6NToidW5pdGUiO3M6MToiaCI7fX0=",
  "link_lien": "",
  "link_libelle": "",
  "link_picto": "",
  "link_bulle": "",
  "link_photo": "",
  "TCompo": [
    {
      "comp": "MO",
      "cfab": "0000MO",
      "libelle": "Main d'oeuvre",
      "libtech": "Main d'oeuvre",
      "reffabri": "MO",
      "prixt": "0",
      "cond": "0",
      "hausrab": "0",
      "remise": "0",
      "prix": "0",
      "coeff_eco": "1",
      "coeff_std": "1",
      "coeff_luxe": "1",
      "quantite": "0.8",
      "type": "9",
      "unite": "h"
    }
  ],
  "is_active": true
}

BC_OUVRAGES Endpoints

1. Upsert BC Ouvrage

Create or update a bc_ouvrages entry.

Endpoint: POST /api/bc_ouvrages/upsert

Request Body:

{
  "id": 207141,
  "id_parent": "69490",
  "type": "expert",
  "code_ouvrage": 207141,
  "libtech": "Description technique",
  "libelle": "Libellé principal",
  "libcom": "Commentaire",
  "unite": "U",
  "prix": 47.728,
  "compos": "base64_encoded_string",
  "link_lien": "",
  "link_libelle": "",
  "link_picto": "",
  "link_bulle": "",
  "link_photo": "",
  "TCompo": [...],
  "is_active": true
}

Response:

{
  "success": true,
  "id": 207141,
  "libelle": "Libellé principal",
  "code_ouvrage": 207141,
  "operation": "inserted" // or "updated"
}

cURL Example:

curl -X POST https://bp.groupedsd.net/api/bc_ouvrages/upsert \
  -H "Content-Type: application/json" \
  -d '{
    "id": 207141,
    "id_parent": "69490",
    "type": "expert",
    "code_ouvrage": 207141,
    "libelle": "Test ouvrage",
    "TCompo": [{"comp": "MO", "quantite": "0.8"}]
  }'

2. Get All BC Ouvrages

Retrieve all bc_ouvrages entries with optional filtering.

Endpoint: GET /api/bc_ouvrages

Query Parameters: - is_active (optional): Filter by active status (default: true) - id_parent (optional): Filter by parent ID - type (optional): Filter by type (e.g., "expert")

Response:

[
  {
    "id": 207141,
    "id_parent": "69490",
    "type": "expert",
    "code_ouvrage": 207141,
    "libelle": "...",
    "created_at": "2024-01-15T10:30:00",
    "updated_at": "2024-01-15T10:30:00",
    "is_active": true,
    ...
  }
]

cURL Examples:

# Get all active records
curl https://bp.groupedsd.net/api/bc_ouvrages

# Get all records including inactive
curl https://bp.groupedsd.net/api/bc_ouvrages?is_active=false

# Get by parent ID
curl https://bp.groupedsd.net/api/bc_ouvrages?id_parent=69490

# Get by type
curl https://bp.groupedsd.net/api/bc_ouvrages?type=expert

3. Get Single BC Ouvrage

Retrieve a specific bc_ouvrages entry by ID.

Endpoint: GET /api/bc_ouvrages/<id>

Response:

{
  "id": 207141,
  "id_parent": "69490",
  "type": "expert",
  "code_ouvrage": 207141,
  "libtech": "...",
  "libelle": "...",
  "tcompo": [...],
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true,
  ...
}

cURL Example:

curl https://bp.groupedsd.net/api/bc_ouvrages/207141

4. Soft Delete BC Ouvrage

Soft delete a bc_ouvrages entry (sets is_active = false).

Endpoint: DELETE /api/bc_ouvrages/<id>

Response:

{
  "success": true,
  "id": 207141,
  "message": "Record soft deleted"
}

cURL Example:

curl -X DELETE https://bp.groupedsd.net/api/bc_ouvrages/207141

Table: bc_familles

Data Model

{
  "id": 206743,
  "id_parent": 48629,
  "libtech": "",
  "libelle": "Petites interventions et travaux préliminaires",
  "link_lien": "",
  "link_libelle": "",
  "link_picto": "",
  "link_bulle": "",
  "tag_c2e": "0",
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true
}

BC_FAMILLES Endpoints

1. Upsert BC Famille

Create or update a bc_familles entry.

Endpoint: POST /api/bc_familles/upsert

Request Body:

{
  "id": 206743,
  "id_parent": 48629,
  "libtech": "",
  "libelle": "Petites interventions et travaux préliminaires",
  "link_lien": "",
  "link_libelle": "",
  "link_picto": "",
  "link_bulle": "",
  "tag_c2e": "0",
  "is_active": true
}

Response:

{
  "success": true,
  "id": 206743,
  "libelle": "Petites interventions et travaux préliminaires",
  "id_parent": 48629,
  "operation": "inserted"
}

cURL Example:

curl -X POST https://bp.groupedsd.net/api/bc_familles/upsert \
  -H "Content-Type: application/json" \
  -d '{
    "id": 206743,
    "id_parent": 48629,
    "libelle": "Petites interventions et travaux préliminaires",
    "tag_c2e": "0"
  }'

2. Get All BC Familles

Retrieve all bc_familles entries with optional filtering.

Endpoint: GET /api/bc_familles

Query Parameters: - is_active (optional): Filter by active status (default: true) - id_parent (optional): Filter by parent ID (use 'null' for root categories) - tag_c2e (optional): Filter by C2E tag

Response:

[
  {
    "id": 206743,
    "id_parent": 48629,
    "libtech": "",
    "libelle": "Petites interventions et travaux préliminaires",
    "tag_c2e": "0",
    "created_at": "2024-01-15T10:30:00",
    "updated_at": "2024-01-15T10:30:00",
    "is_active": true
  }
]

cURL Examples:

# Get all active familles
curl https://bp.groupedsd.net/api/bc_familles

# Get root categories (no parent)
curl https://bp.groupedsd.net/api/bc_familles?id_parent=null

# Get by parent ID
curl https://bp.groupedsd.net/api/bc_familles?id_parent=48629

# Get by C2E tag
curl https://bp.groupedsd.net/api/bc_familles?tag_c2e=0

3. Get Single BC Famille

Retrieve a specific bc_familles entry by ID.

Endpoint: GET /api/bc_familles/<id>

Response:

{
  "id": 206743,
  "id_parent": 48629,
  "libtech": "",
  "libelle": "Petites interventions et travaux préliminaires",
  "tag_c2e": "0",
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true
}

cURL Example:

curl https://bp.groupedsd.net/api/bc_familles/206743

4. Get Famille Children

Get all child categories of a specific famille (subcategories).

Endpoint: GET /api/bc_familles/<id>/children

Response:

[
  {
    "id": 206744,
    "id_parent": 206743,
    "libelle": "Subcategory 1",
    "is_active": true
  },
  {
    "id": 206745,
    "id_parent": 206743,
    "libelle": "Subcategory 2",
    "is_active": true
  }
]

cURL Example:

curl https://bp.groupedsd.net/api/bc_familles/206743/children

5. Soft Delete BC Famille

Soft delete a bc_familles entry (sets is_active = false).

Endpoint: DELETE /api/bc_familles/<id>

Response:

{
  "success": true,
  "id": 206743,
  "message": "Record soft deleted"
}

cURL Example:

curl -X DELETE https://bp.groupedsd.net/api/bc_familles/206743

Statistics Endpoint

Get API Statistics

Endpoint: GET /api/stats

Response:

{
  "api_operations": {
    "post_count": 10,
    "put_count": 5,
    "upsert_count": 20,
    "upsert_inserted": 15,
    "upsert_updated": 5,
    "bc_ouvrages_upsert_count": 8,
    "bc_ouvrages_inserted": 6,
    "bc_ouvrages_updated": 2,
    "bc_familles_upsert_count": 12,
    "bc_familles_inserted": 10,
    "bc_familles_updated": 2
  },
  "database": {
    "total_records": 150
  }
}

cURL Example:

curl https://bp.groupedsd.net/api/stats | jq

Reset Statistics

Endpoint: POST /api/stats/reset

Response:

{
  "success": true,
  "message": "Statistics reset"
}

cURL Example:

curl -X POST https://bp.groupedsd.net/api/stats/reset

Database Setup

Create the BC_OUVRAGES Table

Run the SQL script:

psql -U postgres -d dsd_data -f backend/create_bc_ouvrages.sql

Or manually connect and run:

psql -U postgres -d dsd_data

Then paste the contents of create_bc_ouvrages.sql.

Verify Table Creation

-- Check table structure
\d dsd_data.bc_ouvrages

-- Count records
SELECT COUNT(*) FROM dsd_data.bc_ouvrages;

-- View sample records
SELECT id, libelle, type, prix, is_active
FROM dsd_data.bc_ouvrages
LIMIT 5;

XHR Interceptor

The Firefox extension includes an XHR/Fetch interceptor that automatically captures API responses and saves data to the database.

Monitored Endpoints

  1. /api/bcdf/getFamille/ - Captures famille (category) data
  2. Automatically saves to dsd_data.bc_familles table
  3. Triggered when clicking tree nodes in the batichiffrage interface

  4. /api/bcdf/getOuvrages/ - Captures ouvrage (work item) data

  5. Automatically saves to dsd_data.bc_ouvrages table
  6. Triggered when navigating to work item details

Usage

// Enable auto-save (required for automatic data capture)
window.xhrInterceptor.enableAutoSave()

// Disable auto-save
window.xhrInterceptor.disableAutoSave()

// Progress popup controls
window.xhrInterceptor.enableProgressPopup()   // Enable visual progress (default: enabled)
window.xhrInterceptor.disableProgressPopup()  // Disable visual progress
window.xhrInterceptor.showProgress()          // Manually show progress popup
window.xhrInterceptor.hideProgress()          // Manually hide progress popup

// Check statistics
window.xhrInterceptor.getStats()
// Returns:
// {
//   intercepted: 25,
//   saved: 25,
//   errors: 0,
//   autoSaveEnabled: true,
//   showProgressPopup: true,
//   familles: { intercepted: 10, saved: 10, errors: 0 },
//   ouvrages: { intercepted: 15, saved: 15, errors: 0 }
// }

// Reset statistics
window.xhrInterceptor.resetStats()

// Test save functions
window.xhrInterceptor.testSaveFamille({id: 12345, libelle: "Test"})
window.xhrInterceptor.testSaveOuvrage({id: 67890, libelle: "Test Ouvrage"})

Progress Popup

When auto-save is enabled, a visual progress popup appears in the top-right corner showing: - Progress bar - Visual percentage complete - Current item - Name of the item being processed - Statistics - Real-time counts of saved familles, ouvrages, and errors - Auto-hide - Automatically disappears 3 seconds after completion

The popup can be: - Closed manually - Click the Ă— button - Disabled - window.xhrInterceptor.disableProgressPopup() - Re-enabled - window.xhrInterceptor.enableProgressPopup()

Console Output

When navigating the batichiffrage interface with auto-save enabled:

[XHR Interceptor] Intercepted getFamille request: /api/bcdf/getFamille/48629
[XHR Interceptor] Processing getFamille response...
[XHR Interceptor] Found 3 familles to process
[XHR Interceptor] ✓ Saved famille: Électricité (inserted)
[XHR Interceptor] âś“ Saved famille: Plomberie (updated)
[XHR Interceptor] Finished processing familles. Total saved: 2, Errors: 0

[XHR Interceptor] Intercepted getOuvrages request: /api/bcdf/getOuvrages/71282
[XHR Interceptor] Processing getOuvrages response...
[XHR Interceptor] Found 12 ouvrages to process
[XHR Interceptor] ✓ Saved ouvrage: Dépose d'un battant de volets (inserted)
[XHR Interceptor] Finished processing ouvrages. Total saved: 12, Errors: 0

Important Notes


Notes

JSONB Field: tcompo

The TCompo array is stored as JSONB for efficient querying. You can query it using PostgreSQL JSONB operators:

-- Find ouvrages with specific component
SELECT * FROM dsd_data.bc_ouvrages
WHERE tcompo @> '[{"comp": "MO"}]';

-- Extract component quantities
SELECT id, libelle,
       jsonb_path_query(tcompo, '$[*].quantite') as quantities
FROM dsd_data.bc_ouvrages;

Linearized TCompo Views

For easier analysis, two views are available that flatten the TCompo JSONB structure:

1. bc_ouvrages_tcompo_linearized

Each TCompo component becomes a separate row with all fields extracted as columns.

-- View all components
SELECT * FROM dsd_data.bc_ouvrages_tcompo_linearized LIMIT 10;

-- Find all "Main d'oeuvre" components
SELECT ouvrage_id, ouvrage_libelle, quantite, component_unite
FROM dsd_data.bc_ouvrages_tcompo_linearized
WHERE comp = 'MO';

-- Calculate total quantity per component type
SELECT comp, component_libelle, SUM(quantite) as total_qty, COUNT(*) as usage_count
FROM dsd_data.bc_ouvrages_tcompo_linearized
GROUP BY comp, component_libelle
ORDER BY usage_count DESC;

-- Get all components for a specific ouvrage
SELECT * FROM dsd_data.bc_ouvrages_tcompo_linearized
WHERE ouvrage_id = 212632;

Available columns: - ouvrage_id, id_parent, ouvrage_type, code_ouvrage - ouvrage_libelle, ouvrage_unite, ouvrage_prix - comp, cfab, component_libelle, libtech, reffabri - prixt, cond, hausrab, remise, component_prix - coeff_eco, coeff_std, coeff_luxe - quantite, component_type, component_unite - created_at, updated_at, is_active

2. bc_ouvrages_tcompo_summary

Summary statistics per ouvrage showing component counts and totals.

-- View summary for all ouvrages
SELECT * FROM dsd_data.bc_ouvrages_tcompo_summary
ORDER BY component_count DESC;

-- Find ouvrages with most components
SELECT ouvrage_libelle, component_count, mo_count, materiel_count
FROM dsd_data.bc_ouvrages_tcompo_summary
WHERE component_count > 5;

Available columns: - ouvrage_id, code_ouvrage, ouvrage_libelle, ouvrage_prix - component_count - Total number of components - mo_count - Count of "Main d'oeuvre" (MO) components - materiel_count - Count of material components (type = '2') - total_quantite - Sum of all component quantities - created_at, is_active

Creating the Views

Run the SQL script to create these views:

psql -U postgres -d dsd_data -f backend/linearize_tcompo.sql

Soft Delete Pattern

Records are never physically deleted. Instead, is_active is set to false. This allows: - Data recovery - Audit trails - Historical analysis

To permanently delete (use with caution):

DELETE FROM dsd_data.bc_ouvrages WHERE id = 207141;

Timestamps


Batiprix API

BP Tables Overview

  1. bp_categories - Nomenclature hierarchy extracted from HTML
  2. bp_familles - Parent category hierarchy from API responses
  3. bp_ouvrages - Work items with complete pricing data
  4. bp_ouvrages_elements - Component elements within work items
  5. bp_chiffrages_elements - Pricing/quote section elements

Table: bp_categories

Data Model

{
  "id": 12345,
  "id_parent": 12340,
  "nomenclature": "01 07",
  "title": "Terrassement pour fondations",
  "code": "01 07",
  "level": 2,
  "is_leaf": false,
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true
}

BP_CATEGORIES Endpoints

1. Upsert BP Category

Create or update a bp_categories entry.

Endpoint: POST /api/bp/categories/upsert

Request Body:

{
  "id": 12345,
  "id_parent": 12340,
  "nomenclature": "01 07",
  "title": "Terrassement pour fondations",
  "code": "01 07",
  "level": 2,
  "is_leaf": false,
  "is_active": true
}

Response:

{
  "success": true,
  "id": 12345,
  "title": "Terrassement pour fondations",
  "nomenclature": "01 07",
  "operation": "inserted"
}

2. Get All BP Categories

Retrieve all bp_categories entries with optional filtering.

Endpoint: GET /api/bp/categories

Query Parameters: - is_active (optional): Filter by active status (default: true) - id_parent (optional): Filter by parent ID (use 'null' for root categories) - level (optional): Filter by hierarchy level - is_leaf (optional): Filter by leaf status

cURL Examples:

# Get all active categories
curl https://bp.groupedsd.net/api/bp/categories

# Get root categories
curl https://bp.groupedsd.net/api/bp/categories?id_parent=null

# Get leaf categories only
curl https://bp.groupedsd.net/api/bp/categories?is_leaf=true

3. Get Single BP Category

Endpoint: GET /api/bp/categories/<id>

4. Get Category Children

Endpoint: GET /api/bp/categories/<id>/children

5. Soft Delete BP Category

Endpoint: DELETE /api/bp/categories/<id>


Table: bp_familles

Data Model

{
  "id": 67890,
  "title": "Travaux de fondation",
  "child_count": 5,
  "has_children": true,
  "parent_id": 67880,
  "level": 2,
  "code": "01 07",
  "nomenclature": "01 07",
  "ce_code": "CEP-001",
  "descriptif": "Description détaillée des travaux",
  "moe": 0.15,
  "fgds": 0.10,
  "ben": 0.08,
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true
}

BP_FAMILLES Endpoints

1. Upsert BP Famille

Create or update a bp_familles entry. Supports both uppercase (from JSON API) and lowercase field names.

Endpoint: POST /api/bp/familles/upsert

Request Body:

{
  "Id": 67890,
  "Title": "Travaux de fondation",
  "ChildCount": 5,
  "HasChildren": true,
  "ParentId": 67880,
  "Level": 2,
  "Code": "01 07",
  "Nomenclature": "01 07",
  "CECode": "CEP-001",
  "Descriptif": "Description détaillée",
  "Moe": 0.15,
  "Fgds": 0.10,
  "Ben": 0.08
}

Response:

{
  "success": true,
  "id": 67890,
  "title": "Travaux de fondation",
  "nomenclature": "01 07",
  "operation": "inserted"
}

2. Get All BP Familles

Endpoint: GET /api/bp/familles

Query Parameters: - is_active (optional): Filter by active status (default: true) - parent_id (optional): Filter by parent ID - level (optional): Filter by hierarchy level

3. Get Single BP Famille

Endpoint: GET /api/bp/familles/<id>

4. Get Famille Parents (Hierarchy)

Get the full parent hierarchy chain for a famille.

Endpoint: GET /api/bp/familles/<id>/parents

Response:

[
  {"id": 67800, "title": "Gros œuvre", "level": 0},
  {"id": 67880, "title": "Fondations", "level": 1},
  {"id": 67890, "title": "Travaux de fondation", "level": 2}
]

5. Soft Delete BP Famille

Endpoint: DELETE /api/bp/familles/<id>


Table: bp_ouvrages

Data Model

{
  "id": 1,
  "ouvrage_id": 123456,
  "nomenclature_id": 12345,
  "code": "01 07 06 03 00 003",
  "title": "Béton de propreté dosé à 150 kg/m3",
  "unit": "MÂł",
  "labor_time": 0.850,
  "labor_cost": 45.50,
  "supply_unit": "MÂł",
  "supply_quantity": 1.050,
  "supply_unit_price": 85.00,
  "supply_total": 89.25,
  "cost_price_ht": 134.75,
  "supply_install_price": 156.80,
  "selling_price_tier1": 178.50,
  "selling_price_tier2": 195.00,
  "install_only_price": 52.50,
  "millesime": 2024,
  "is_custom_work": false,
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00",
  "is_active": true
}

Field Descriptions

Field Description
ouvrage_id Unique identifier from Batiprix (data-idouvrage)
nomenclature_id Links to bp_categories
code Batiprix nomenclature code (e.g., "01 07 06 03 00 003")
labor_time Average labor time in hours
labor_cost Labor cost including social charges
supply_unit Unit for supplies
supply_quantity Supply quantity needed
supply_unit_price Unit price for supplies
supply_total Total supplies cost
cost_price_ht Dry cost / Cost price HT
supply_install_price Supplies + installation price
selling_price_tier1 Selling price HT (supply + install) - tier 1
selling_price_tier2 Target selling price HT - tier 2
install_only_price Installation only price
millesime Price list version/year
is_custom_work Whether this is a custom work item

BP_OUVRAGES Endpoints

1. Upsert BP Ouvrage

Create or update a bp_ouvrages entry with complete pricing data.

Endpoint: POST /api/bp/ouvrages/upsert

Request Body:

{
  "ouvrage_id": 123456,
  "nomenclature_id": 12345,
  "code": "01 07 06 03 00 003",
  "title": "Béton de propreté dosé à 150 kg/m3",
  "unit": "MÂł",
  "labor_time": 0.850,
  "labor_cost": 45.50,
  "supply_unit": "MÂł",
  "supply_quantity": 1.050,
  "supply_unit_price": 85.00,
  "supply_total": 89.25,
  "cost_price_ht": 134.75,
  "supply_install_price": 156.80,
  "selling_price_tier1": 178.50,
  "selling_price_tier2": 195.00,
  "install_only_price": 52.50,
  "millesime": 2024,
  "is_custom_work": false
}

Response:

{
  "success": true,
  "ouvrage_id": 123456,
  "title": "Béton de propreté dosé à 150 kg/m3",
  "code": "01 07 06 03 00 003",
  "operation": "inserted"
}

2. Get All BP Ouvrages

Endpoint: GET /api/bp/ouvrages

Query Parameters: - is_active (optional): Filter by active status (default: true) - category_id (optional): Filter by category

3. Get Single BP Ouvrage

Endpoint: GET /api/bp/ouvrages/<id>

4. Soft Delete BP Ouvrage

Endpoint: DELETE /api/bp/ouvrages/<id>


Table: bp_ouvrages_elements

Component elements within work items.

Data Model

{
  "id": 1,
  "ouvrage_id": 123456,
  "description": "Ciment CEM II/A 32,5",
  "unit": "kg",
  "quantity": 150,
  "unit_price": 0.15,
  "total_price": 22.50,
  "element_order": 1,
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00"
}

BP_OUVRAGES_ELEMENTS Endpoints

1. Upsert BP Ouvrage Element

Endpoint: POST /api/bp/ouvrages-elements/upsert

Request Body:

{
  "ouvrage_id": 123456,
  "description": "Ciment CEM II/A 32,5",
  "unit": "kg",
  "quantity": 150,
  "unit_price": 0.15,
  "total_price": 22.50,
  "element_order": 1
}

2. Get Elements for Ouvrage

Endpoint: GET /api/bp/ouvrages-elements/<ouvrage_id>

Returns all elements for a specific ouvrage, ordered by element_order.


Table: bp_chiffrages_elements

Pricing/quote section elements with hierarchical structure.

Data Model

{
  "id": 1,
  "section_id": 98765,
  "parent_id": 98760,
  "text": "Section Gros œuvre",
  "description": "Travaux de gros œuvre",
  "level": 1,
  "is_directory": true,
  "has_items": true,
  "has_sections": true,
  "has_ouvrages": false,
  "total_price": 15000.00,
  "gross_total_price": 16500.00,
  "total_price_vat": 19800.00,
  "discount": 0,
  "discount_type": 1,
  "vat_type": 1,
  "quantity": 1,
  "nomenclature": "01",
  "code": 1,
  "libelle": "Gros œuvre",
  "prix_de_vente": 15000.00,
  "order_num": 1,
  "is_custom_work": false,
  "created_at": "2024-01-15T10:30:00",
  "updated_at": "2024-01-15T10:30:00"
}

BP_CHIFFRAGES_ELEMENTS Endpoints

1. Upsert BP Chiffrage Element

Create or update a chiffrage element. Supports field names from Batiprix JSON responses.

Endpoint: POST /api/bp/chiffrages-elements/upsert

Request Body (JSON from Batiprix):

{
  "id": 98765,
  "parentId": 98760,
  "text": "Section Gros œuvre",
  "description": "Travaux de gros œuvre",
  "level": 1,
  "isDirectory": true,
  "hasItems": true,
  "hasSections": true,
  "hasOuvrages": false,
  "TotalPrice": 15000.00,
  "GrossTotalPrice": 16500.00,
  "TotalPriceVat": 19800.00,
  "Discount": 0,
  "DiscountType": 1,
  "VatType": 1,
  "quantity": 1,
  "Nomenclature": "01",
  "Code": 1,
  "Libelle": "Gros œuvre",
  "PrixDeVente": 15000.00,
  "Order": 1,
  "IsCustomWork": false
}

Response:

{
  "success": true,
  "id": 1,
  "section_id": 98765,
  "text": "Section Gros œuvre",
  "label": "Gros œuvre",
  "operation": "inserted"
}

2. Get All BP Chiffrage Elements

Endpoint: GET /api/bp/chiffrages-elements

Query Parameters: - parent_id (optional): Filter by parent section ID (use 'null' for root) - level (optional): Filter by hierarchy level

3. Get Single BP Chiffrage Element

Endpoint: GET /api/bp/chiffrages-elements/<section_id>

4. Get Element Children

Endpoint: GET /api/bp/chiffrages-elements/<section_id>/children

5. Delete BP Chiffrage Element

Hard delete (not soft delete) a chiffrage element.

Endpoint: DELETE /api/bp/chiffrages-elements/<section_id>


Batiprix Database Setup

Create Tables

Run the SQL script:

psql -U postgres -d dsd_data -f backend/create_bp_tables.sql

Run Migrations

psql -U postgres -d dsd_data -f backend/migrations/008_create_bp_ouvrage_elements.sql
psql -U postgres -d dsd_data -f backend/migrations/009_create_bp_chiffrages_elements.sql

Verify Tables

-- Check bp_ouvrages structure
\d dsd_data.bp_ouvrages

-- Count records
SELECT
    (SELECT COUNT(*) FROM dsd_data.bp_categories) as categories,
    (SELECT COUNT(*) FROM dsd_data.bp_familles) as familles,
    (SELECT COUNT(*) FROM dsd_data.bp_ouvrages) as ouvrages;

-- View sample ouvrages with pricing
SELECT ouvrage_id, code, title, selling_price_tier2
FROM dsd_data.bp_ouvrages
LIMIT 5;

ProgBat API

PG Tables Overview

  1. pg_fournitures - Supply/material items from ProgBat library
  2. pg_ouvrages - Work items/services from ProgBat library
  3. pg_textes - Text templates from ProgBat library

Table: pg_fournitures

Data Model

{
  "id": 1,
  "progbat_id": 4,
  "identifiant": "BT12 0000",
  "type_element": "Fournitures",
  "libelle": "Fourniture et pose de tuiles en terre cuite...",
  "famille_metier": "",
  "marque": "Wienerberger",
  "produit": "Tradi 12",
  "gencode": "BT12 0000",
  "prix_achat": 1.30,
  "unite": "U",
  "marge": "150%",
  "prix_vente": 3.25,
  "mis_a_jour_le": "2022-04-04T10:05:00",
  "modele_calcul_quantite": "-",
  "taux_tva_achat": "20%",
  "controle": true,
  "lien": "/elements/4",
  "is_active": true,
  "base_source": "progbat"
}

PG_FOURNITURES Endpoints

1. Upsert PG Fourniture

Create or update a pg_fournitures entry.

Endpoint: POST /api/pg/fournitures/upsert

Request Body (from Excel or lowercase):

{
  "ID": 4,
  "Identifiant": "BT12 0000",
  "Type d'élément": "Fournitures",
  "Libellé": "Fourniture et pose de tuiles...",
  "Famille métier": "",
  "Marque": "Wienerberger",
  "Produit": "Tradi 12",
  "Gencode": "BT12 0000",
  "Prix d'achat": "1,3",
  "Unité": "U",
  "Marge": "150%",
  "Prix de vente": "3,25",
  "Contrôlé": "Oui",
  "Lien": "/elements/4"
}

Response:

{
  "success": true,
  "id": 1,
  "progbat_id": 4,
  "identifiant": "BT12 0000",
  "libelle": "Fourniture et pose de tuiles...",
  "operation": "inserted",
  "base_source": "progbat"
}

2. Get All PG Fournitures

Endpoint: GET /api/pg/fournitures

Query Parameters: - is_active (optional): Filter by active status (default: true) - famille_metier (optional): Filter by famille métier

3. Get Single PG Fourniture

Endpoint: GET /api/pg/fournitures/<progbat_id>

4. Soft Delete PG Fourniture

Endpoint: DELETE /api/pg/fournitures/<progbat_id>


Table: pg_ouvrages

Data Model

{
  "id": 1,
  "progbat_id": 3,
  "identifiant": "1010",
  "libelle": "Fourniture et pose de tuiles en terre cuite...",
  "famille_metier": "",
  "unite": "U",
  "debourse_ht": 32.78,
  "marge": "150%",
  "prix_vente": 81.95,
  "modele_calcul_quantite": "-",
  "controle": false,
  "lien": "/structures/3",
  "is_active": true,
  "base_source": "progbat"
}

PG_OUVRAGES Endpoints

1. Upsert PG Ouvrage

Create or update a pg_ouvrages entry.

Endpoint: POST /api/pg/ouvrages/upsert

Request Body (from Excel or lowercase):

{
  "ID": 3,
  "Identifiant": "1010",
  "Libellé": "Fourniture et pose de tuiles...",
  "Famille métier": "",
  "Unité": "U",
  "Déboursé H.T": "32,7793103448",
  "Marge": "150%",
  "Prix de vente": "81,948",
  "Contrôlé": "Non",
  "Lien": "/structures/3"
}

Response:

{
  "success": true,
  "id": 1,
  "progbat_id": 3,
  "identifiant": "1010",
  "libelle": "Fourniture et pose de tuiles...",
  "operation": "inserted",
  "base_source": "progbat"
}

2. Get All PG Ouvrages

Endpoint: GET /api/pg/ouvrages

Query Parameters: - is_active (optional): Filter by active status (default: true) - famille_metier (optional): Filter by famille métier

3. Get Single PG Ouvrage

Endpoint: GET /api/pg/ouvrages/<progbat_id>

4. Soft Delete PG Ouvrage

Endpoint: DELETE /api/pg/ouvrages/<progbat_id>


Table: pg_textes

Data Model

{
  "id": 1,
  "progbat_id": 1121,
  "identifiant": "Exemple gestion des déchets",
  "contenu": "Gestion des déchets : décret n°2020-1817...",
  "lien": "/text-elements/1121",
  "is_active": true,
  "base_source": "progbat"
}

PG_TEXTES Endpoints

1. Upsert PG Texte

Endpoint: POST /api/pg/textes/upsert

2. Get All PG Textes

Endpoint: GET /api/pg/textes

3. Get Single PG Texte

Endpoint: GET /api/pg/textes/<progbat_id>

4. Soft Delete PG Texte

Endpoint: DELETE /api/pg/textes/<progbat_id>


ProgBat Import Script

Import data from the ProgBat Excel file:

# First, create the tables
psql -U postgres -d dsd_data -f backend/create_pg_tables.sql

# Then import the data via API
python backend/import_progbat.py --api-url https://api.bp.groupedsd.net/api