This document describes the REST API for three construction pricing databases: Batichiffrage, Batiprix, and ProgBat.
https://bp.groupedsd.net # Full application (frontend + API)
https://api.bp.groupedsd.net # API-only (recommended for integrations)
/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
/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
/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
bc_ouvrages - Detailed work items with compositions and pricingbc_familles - Category/family hierarchy for organizing work itemsbc_ouvrages{
"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
}
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"}]
}'
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
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
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
bc_familles{
"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
}
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"
}'
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
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
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
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
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
Endpoint: POST /api/stats/reset
Response:
{
"success": true,
"message": "Statistics reset"
}
cURL Example:
curl -X POST https://bp.groupedsd.net/api/stats/reset
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.
-- 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;
The Firefox extension includes an XHR/Fetch interceptor that automatically captures API responses and saves data to the database.
/api/bcdf/getFamille/ - Captures famille (category) datadsd_data.bc_familles tableTriggered when clicking tree nodes in the batichiffrage interface
/api/bcdf/getOuvrages/ - Captures ouvrage (work item) data
dsd_data.bc_ouvrages table// 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"})
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()
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
tcompoThe 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;
For easier analysis, two views are available that flatten the TCompo JSONB structure:
bc_ouvrages_tcompo_linearizedEach 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
bc_ouvrages_tcompo_summarySummary 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
Run the SQL script to create these views:
psql -U postgres -d dsd_data -f backend/linearize_tcompo.sql
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;
created_at: Set automatically on INSERTupdated_at: Automatically updated on every UPDATE via triggerbp_categories - Nomenclature hierarchy extracted from HTMLbp_familles - Parent category hierarchy from API responsesbp_ouvrages - Work items with complete pricing databp_ouvrages_elements - Component elements within work itemsbp_chiffrages_elements - Pricing/quote section elementsbp_categories{
"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
}
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"
}
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
Endpoint: GET /api/bp/categories/<id>
Endpoint: GET /api/bp/categories/<id>/children
Endpoint: DELETE /api/bp/categories/<id>
bp_familles{
"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
}
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"
}
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
Endpoint: GET /api/bp/familles/<id>
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}
]
Endpoint: DELETE /api/bp/familles/<id>
bp_ouvrages{
"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 | 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 |
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"
}
Endpoint: GET /api/bp/ouvrages
Query Parameters:
- is_active (optional): Filter by active status (default: true)
- category_id (optional): Filter by category
Endpoint: GET /api/bp/ouvrages/<id>
Endpoint: DELETE /api/bp/ouvrages/<id>
bp_ouvrages_elementsComponent elements within work items.
{
"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"
}
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
}
Endpoint: GET /api/bp/ouvrages-elements/<ouvrage_id>
Returns all elements for a specific ouvrage, ordered by element_order.
bp_chiffrages_elementsPricing/quote section elements with hierarchical structure.
{
"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"
}
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"
}
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
Endpoint: GET /api/bp/chiffrages-elements/<section_id>
Endpoint: GET /api/bp/chiffrages-elements/<section_id>/children
Hard delete (not soft delete) a chiffrage element.
Endpoint: DELETE /api/bp/chiffrages-elements/<section_id>
Run the SQL script:
psql -U postgres -d dsd_data -f backend/create_bp_tables.sql
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
-- 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;
pg_fournitures - Supply/material items from ProgBat librarypg_ouvrages - Work items/services from ProgBat librarypg_textes - Text templates from ProgBat librarypg_fournitures{
"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"
}
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"
}
Endpoint: GET /api/pg/fournitures
Query Parameters:
- is_active (optional): Filter by active status (default: true)
- famille_metier (optional): Filter by famille métier
Endpoint: GET /api/pg/fournitures/<progbat_id>
Endpoint: DELETE /api/pg/fournitures/<progbat_id>
pg_ouvrages{
"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"
}
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"
}
Endpoint: GET /api/pg/ouvrages
Query Parameters:
- is_active (optional): Filter by active status (default: true)
- famille_metier (optional): Filter by famille métier
Endpoint: GET /api/pg/ouvrages/<progbat_id>
Endpoint: DELETE /api/pg/ouvrages/<progbat_id>
pg_textes{
"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"
}
Endpoint: POST /api/pg/textes/upsert
Endpoint: GET /api/pg/textes
Endpoint: GET /api/pg/textes/<progbat_id>
Endpoint: DELETE /api/pg/textes/<progbat_id>
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