2891 listAllAirtableRecords(AIRTABLE_CONSO.suppliers_table_id), 2892 listAllAirtableRecords(AIRTABLE_CONSO.sales_table_id), 2893 loadAllRows(context, 'offer_remuneration_rules', [ 2894 'id', 'local_id', 'offre_id', 'vente_type', 'prime_montant', 2895 'prime_devise', 'active', 'starts_at', 'ends_at', 'scope_type', 'scope_id', 'payout_mode', 2896 ]), 2897 loadAllRows(context, 'commerciaux', [ 2898 'id', 'email', 'centre_appel_id', 'provider_user_id', 'provider_email', 'active', 2899 ]), 2900 ]); 2901 return applyAirtableRemunerationRules( 2902 mapAirtableConsoPiloteVentes(context, supplierRecords, salesRecords, commercialRows), 2903 remunerationRules 2904 ); 2905 } catch (e) { 2906 context.log(`[airtable conso] skipped: ${String(e?.message || e).slice(0, 300)}`); 2907 return []; 2908 } 2909} 2910 2911function identityPart(value) { 2912 const s = String(value ?? '').trim(); 2913 if (!s) return ''; 2914 return s 2915 .normalize('NFD').replace(/[\u0300-\u036f]/g, '') 2916 .toLowerCase() 2917 .replace(/[^a-z0-9]+/g, '_') 2918 .replace(/^_+|_+$/g, '') 2919 .slice(0, 80); 2920} 2921 2922function preciseTelecomOfferId(defaultOfferId, typeValue) { 2923 const parent = String(defaultOfferId || '').trim(); 2924 const type = identityPart(typeValue); 2925 if (!type) return parent; 2926 if (parent === 'free_box') { 2927 if (type.includes('revolution_light')) return 'free_box_revolution_light'; 2928 if (type.includes('pop_s')) return 'free_box_pop_s'; 2929 if (type.includes('ultra_essentiel')) return 'free_box_ultra_essentiel'; 2930 if (type.includes('ultra')) return 'free_box_ultra'; 2931 if (type.includes('5g')) return 'free_box_5g'; 2932 if (type.includes('pop')) return 'free_box_pop'; 2933 } 2934 if (parent === 'free_mobile') { 2935 if (type.includes('free_max') || type.includes('forfait_max')) return 'free_mobile_forfait_free_max'; 2936 if (type.includes('serie_free') || type.includes('serie')) return 'free_mobile_serie_free'; 2937 if (type.includes('forfait_free') || type === 'free') return 'free_mobile_forfait_free'; 2938 } 2939 return parent; 2940} 2941function contractIdentityPart(value) { 2942 const parts = Array.isArray(value) ? value : String(value ?? '').split('+'); 2943 return parts 2944 .map(part => { 2945 const raw = String(part ?? '').trim(); 2946 if (!raw) return ''; 2947 const digits = raw.replace(/\D+/g, ''); 2948 return digits.length >= 5 ? digits : identityPart(raw); 2949 }) 2950 .filter(Boolean) 2951 .join('_') 2952 .slice(0, 80); 2953} 2954function canonicalVenteLocalId({ centreId, sheetId, tab, tabCfg, rowNumber, mapped, seg, numeros, offre_id, dateVente }) { 2955 const sheetKey = identityPart(sheetId); 2956 const tabKey = identityPart(tab); 2957 const roleKey = identityPart(tabCfg.role || 'sheet') || 'sheet'; 2958 const segmentKey = identityPart(seg?.contrat_field || seg?.offre_id_default || offre_id || 'single') || 'single'; 2959 const contractKey = contractIdentityPart(numeros || []); 2960 const offreKey = identityPart(offre_id || 'offre') || 'offre'; 2961 if (roleKey === 'form') { 2962 const timestampKey = identityPart(mapped.horodateur || mapped.timestamp || mapped.date_vente || dateVente); 2963 if (timestampKey && contractKey) { 2964 return centreId + '::' + sheetKey + '::' + tabKey + '::form::' + timestampKey + '::' + segmentKey + '::' + contractKey; 2965 } 2966 } 2967 if (dateVente && contractKey) { 2968 return centreId + '::' + sheetKey + '::' + tabKey + '::' + roleKey + '::' + dateVente + '::' + offreKey + '::' + contractKey; 2969 } 2970 const rowKey = rowNumber ? ('r' + rowNumber) : 'row_unknown';