@bisondesk/core-sdk 1.0.608 → 1.0.610

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/lib/constants.d.ts +1 -0
  2. package/lib/constants.d.ts.map +1 -1
  3. package/lib/constants.js +1 -0
  4. package/lib/constants.js.map +1 -1
  5. package/lib/types/leasing-debtors.d.ts +2 -0
  6. package/lib/types/leasing-debtors.d.ts.map +1 -1
  7. package/lib/types/leasing-debtors.js.map +1 -1
  8. package/lib/types/opportunities.d.ts +6 -1
  9. package/lib/types/opportunities.d.ts.map +1 -1
  10. package/lib/types/opportunities.js +1 -0
  11. package/lib/types/opportunities.js.map +1 -1
  12. package/lib/types/reports-leasing.d.ts +93 -0
  13. package/lib/types/reports-leasing.d.ts.map +1 -0
  14. package/lib/types/reports-leasing.js +11 -0
  15. package/lib/types/reports-leasing.js.map +1 -0
  16. package/lib/types/reports-sales.d.ts +12 -0
  17. package/lib/types/reports-sales.d.ts.map +1 -1
  18. package/lib/types/reports-sales.js.map +1 -1
  19. package/lib/types/tenants.d.ts +2 -0
  20. package/lib/types/tenants.d.ts.map +1 -1
  21. package/lib/types/tenants.js.map +1 -1
  22. package/lib/types/vehicles.d.ts +1 -0
  23. package/lib/types/vehicles.d.ts.map +1 -1
  24. package/lib/types/vehicles.js.map +1 -1
  25. package/lib/utils/accounting.d.ts +6 -0
  26. package/lib/utils/accounting.d.ts.map +1 -0
  27. package/lib/utils/accounting.js +26 -0
  28. package/lib/utils/accounting.js.map +1 -0
  29. package/lib/utils/accounting.test.d.ts +2 -0
  30. package/lib/utils/accounting.test.d.ts.map +1 -0
  31. package/lib/utils/accounting.test.js +93 -0
  32. package/lib/utils/accounting.test.js.map +1 -0
  33. package/package.json +1 -1
  34. package/src/constants.ts +6 -0
  35. package/src/types/leasing-debtors.ts +3 -0
  36. package/src/types/opportunities.ts +12 -0
  37. package/src/types/reports-leasing.ts +186 -0
  38. package/src/types/reports-sales.ts +22 -3
  39. package/src/types/tenants.ts +4 -0
  40. package/src/types/vehicles.ts +1 -0
  41. package/src/utils/accounting.test.ts +115 -0
  42. package/src/utils/accounting.ts +40 -0
  43. package/tsconfig.tsbuildinfo +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"vehicles.js","sourceRoot":"/","sources":["types/vehicles.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AttachmentValue,\n Document,\n LocationValue,\n MultiLangValue,\n PaginatedRequest,\n SearchPermissions,\n ShareEmailRequest,\n ShareWhatsappRequest,\n} from '@bisondesk/commons-sdk/types';\nimport { ReferenceData } from '@bisondesk/commons-sdk/utils';\nimport { BaseEvent } from './internal-events.js';\nimport { VehicleSaleLogisticsStatus } from './opportunities.js';\nimport { OpportunityReservation } from './reservations.js';\nimport { ComplexFilter, Filter } from './search.js';\nimport { Transport } from './transports.js';\nimport { DataRecord } from './utils.js';\nimport { VehicleBusinessOverviewXL } from './vehicle-performance.js';\nimport { VehicleSaleInfo } from './vehicle-sales.js';\n\nexport enum VehicleRoles {\n SALE = 'SALE',\n OWN_USE = 'OWN_USE',\n RENTAL = 'RENTAL',\n BUILD = 'BUILD',\n UNKNOWN = 'UNKNOWN',\n BANK_FINANCE = 'BANK_FINANCE',\n}\n\nexport enum VehicleFuelType {\n Petrol = 'PETROL',\n Diesel = 'DIESEL',\n LPG = 'LPG',\n LPG_G3 = 'LPG_G3',\n Electric = 'ELECTRIC',\n CNG = 'CNG',\n Hydrogen = 'HYDROGEN',\n BioEthanol = 'BIO_ETHANOL',\n BioDiesel = 'BIO_DIESEL',\n E85 = 'E85',\n HCNG = 'HCNG',\n LPG_Petrol = 'LPG_PETROL',\n LPG_Diesel = 'LPG_DIESEL',\n LPG_G3_Petrol = 'LPG_G3_PETROL',\n LPG_G3_Diesel = 'LPG_G3_DIESEL',\n Electric_Diesel = 'ELECTRIC_DIESEL',\n Electric_Petrol = 'ELECTRIC_PETROL',\n Hybrid = 'HYBRID',\n Other = 'OTHER',\n}\n\nexport enum VehicleTachoTypes {\n Analog = 'ANALOG',\n Digital = 'DIGITAL',\n SmartTacho1 = 'SMART_TACHO_1',\n SmartTacho2 = 'SMART_TACHO_2',\n}\n\ntype BaseVehicleEvent = BaseEvent & {\n id: string;\n actionAt: string;\n userId: string;\n tenantId: string;\n};\n\ntype VehicleSuperstructure = {\n carCapacity?: number;\n compartmentCount?: number;\n counter?: boolean;\n crane?: {\n make?: string;\n model?: string;\n position?: 'Behind the cabin' | 'Rear';\n present?: boolean;\n year?: number;\n };\n dimensions?: {\n height?: number;\n length?: number;\n width?: number;\n extended?: string; // L1, L2, ...\n heightened?: string; // H1, H2, ...\n };\n baseVehicle?: SuperstructureBaseVehicle;\n extendable?: boolean;\n highPressurePump?: boolean;\n loadingPlatformHeight?: number;\n pump?: boolean;\n slidingRoof?: boolean;\n tailgate?: {\n capacity?: number;\n make?: string;\n model?: string;\n present?: boolean;\n type?: 'slider_lift' | 'cantilever' | 'column_lift';\n };\n tank?: {\n capacity: number; //litters\n volume?: number; //m3\n };\n temperatureControl?: {\n engineType?: 'diesel' | 'electric' | 'diesel+electric' | 'engine';\n make?: string;\n runningHours?: {\n diesel?: number;\n electric?: number;\n };\n };\n tipper?: {\n tipsBack?: boolean;\n tipsLeft?: boolean;\n tipsRight?: boolean;\n };\n};\n\nexport type NewVehicleRequest = {\n branch: string;\n count: number;\n specs: VehicleExternalInfo;\n description: {\n remarks?: string;\n technicalRemarks?: string;\n };\n salesConditions?: {\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n };\n };\n logistics: {\n parking?: string;\n deliveryType?: VehiclePurchaseDeliveryType;\n pickupAddress?: LocationValue;\n status: VehiclePurchaseLogisticsStatus;\n };\n prices: {\n minimum?: string;\n premium?: string;\n internet?: string;\n };\n media: {\n internalPictures: AttachmentValue[];\n pictures: AttachmentValue[];\n videos: AttachmentValue[];\n docs: Document[];\n engineeringDocs: Document[];\n };\n vinList?: string;\n deal: {\n offerId?: string;\n stockNumber?: string;\n contactId: string;\n orgId: string;\n purchasedBy: string;\n purchaseAssistedBy?: string;\n readyToDeliver?: boolean;\n dealStatus: VehiclePurchaseDealStatus;\n costs: {\n purchase: string;\n purchaseVatPercentage: string;\n commission?: string;\n externalTransport?: string;\n internalTransport?: string;\n externalGarage?: string;\n internalGarage?: string;\n other?: string;\n bonus?: string;\n };\n logistics: {\n availableDate?: string;\n vehicleRole: VehicleRoles;\n };\n };\n};\n\nexport type VehicleCreateEvent = BaseVehicleEvent & {\n action: 'create';\n vehicle: Vehicle;\n previousVehicle?: undefined;\n};\n\nexport type VehicleUpdateEvent = BaseVehicleEvent & {\n action: 'update';\n vehicle: Vehicle;\n previousVehicle: Vehicle;\n};\n\nexport type VehicleDeleteEvent = BaseVehicleEvent & {\n action: 'delete';\n vehicle?: undefined;\n previousVehicle: Vehicle;\n};\n\nexport type VehicleEvent = VehicleCreateEvent | VehicleUpdateEvent | VehicleDeleteEvent;\n\ntype BaseVehiclePurchaseEvent = {\n actionAt: string;\n userId: string;\n tenantId: string;\n branchId: string;\n vehicleId: string;\n};\n\nexport type VehiclePurchaseCreateEvent = BaseVehiclePurchaseEvent & {\n action: 'create';\n data: VehiclePurchase;\n previousData?: undefined;\n};\n\nexport type VehiclePurchaseUpdateEvent = BaseVehiclePurchaseEvent & {\n action: 'update';\n data: VehiclePurchase;\n previousData: VehiclePurchase;\n};\n\nexport type VehiclePurchaseEvent = BaseEvent &\n (VehiclePurchaseCreateEvent | VehiclePurchaseUpdateEvent);\n\nexport type UpdateVehiclesPricesEvent = {\n id: string;\n actionAt: string;\n userId: string;\n tenantId: string;\n priceList: AttachmentValue;\n};\n\nexport type PublicVehicle = Pick<Vehicle, 'external' | 'id'>;\n\nexport type PublicSearchVehicle = {\n vehicle: PublicVehicle;\n};\n\nexport enum SearchVehicleSaleDealStatus {\n NotSold = 'NOT_SOLD',\n Reserved = 'RESERVED',\n Sold = 'SOLD',\n}\n\nexport type SearchVehicle = {\n custom: {\n features: string[];\n sold: boolean;\n isReserved: boolean;\n hasPayments: boolean;\n listedOnWebsite: boolean;\n publishedOnWebsiteAt?: string;\n originalBranchId: string;\n };\n customSortableFields: {\n priceChangedAt?: string;\n checkedInAt?: string;\n saleAgreementAt?: string;\n /** Date the vehicle entered virtual inventory (marketing ?? evaluation ?? purchase-agreed). */\n virtualInventoryStartedAt?: string;\n };\n purchase?: {\n supplierStockNumber?: string;\n readyToDeliver: boolean;\n dealStatus?: VehiclePurchaseDealStatus;\n logisticsStatus?: VehiclePurchaseLogisticsStatus;\n purchasedBy?: string;\n parkingName?: string;\n organizationId: string;\n organizationExternalId?: number;\n fullPaymentSentAt?: string;\n marketingStartedAt?: string;\n arrivedAt?: string;\n };\n reservation?: Pick<OpportunityReservation, 'expiresAt' | 'opportunityId' | 'createdBy'>;\n transports: Transport[];\n vehicle: Vehicle;\n sale: {\n opportunityId?: string;\n externalSaleId?: string;\n soldAt?: string;\n soldBy?: string;\n sellers?: string[]; // opportunity account manager [+ assistant]\n dealStatus: SearchVehicleSaleDealStatus;\n logisticStatus?: VehicleSaleLogisticsStatus;\n };\n};\n\nexport type InputSearchVehicle = SearchVehicle & {\n permissions?: SearchPermissions;\n};\n\nexport type VehicleSearchMeta = ReferenceData & {\n matchingOffers?: {\n count: number;\n filters: {\n conditions: Array<Filter | ComplexFilter>;\n fullText?: string;\n };\n };\n};\n\nexport type PriceChangeEvent = BaseEvent & {\n tenantId: string;\n userId: string;\n vehicleId: string;\n input: {\n previousPrices: VehiclePricingHistoryInput;\n currentPrices: VehiclePricingHistoryInput;\n };\n};\n\nexport type VehiclePricingHistory = {\n pricing: VehiclePricingHistoryInput;\n createdAt: string;\n createdBy: string;\n};\n\nexport type VehiclePricingHistoryInput = VehiclePricing & { internet?: string };\n\nexport type VehiclePricing = {\n premium?: string;\n minimum?: string;\n currency?: string;\n};\n\nexport type RelatedVehicle = {\n vehicleId: string;\n description: string;\n stockNumber: string;\n};\n\nexport type VehicleInternalInfo = {\n createdAt: string;\n description: {\n title: string;\n remarks?: string;\n technicalRemarks?: string;\n };\n identification: {\n administrativeNumber?: string;\n stockNumber: string;\n branch: string;\n customerStockNumber?: string;\n offerId?: string;\n licensePlateHistory?: {\n licensePlate: string;\n changedAt: string;\n }[];\n\n /**\n * This ID is used to link different vehicle objects that represent the same real world asset.\n * Ideally the chassis number could be used, but in some cases it is not available yet.\n */\n metaId: string;\n };\n documents: Document[];\n pictures: AttachmentValue[];\n engineeringDocs: Document[];\n pricing: VehiclePricing;\n role: VehicleRoles;\n status: VehicleStatus;\n updatedAt: string;\n /* use this property to store additional terms that you want the vehicle to be found with when doing a full text search */\n keywords?: string[];\n relatedVehicles: RelatedVehicle[];\n};\n\nexport type VehicleOriginalInfo = {\n /** IMMEDIATE source tenant (the predecessor in a sync chain). */\n tenantId: string;\n tenantChain?: string[];\n identification: {\n id: string;\n branchId: string;\n stockNumber: string;\n metaId?: string;\n country: string;\n };\n salesConditions: {\n leasing?: boolean;\n price?: string;\n currency?: string;\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n };\n /**\n * The immediate source's sellability at the last sync hop: true when the source was not\n * sellable — not in `SALE` role, had an active reservation or external sale, or was itself\n * unavailable. Derived (never set by a human) and refreshed on every cross-tenant update so\n * it cascades down the whole chain.\n */\n unavailable?: boolean;\n };\n /**\n * Deepest-origin tenant/branch in a multi-hop sync chain. Carries forward\n * verbatim across hops so consumers can answer \"where did this vehicle\n * actually originate?\" without walking the chain.\n */\n owner: {\n tenantId: string;\n branchId: string;\n country: string;\n };\n};\n\nexport type VehicleAxlesInfo = {\n details?: VehicleAxle[];\n configuration?: string;\n count?: number;\n poweredAxlesCount?: number;\n wheelbase?: number;\n suspension?: 'Air' | 'Air/Steel' | 'Steel';\n loadCapacity?: number;\n};\n\nexport type SuperstructureBaseVehicle = {\n category: string;\n make: string;\n model?: string;\n type?: string;\n axlesConfiguration?: string;\n emissionsClass?: string;\n transmissionType?: 'Automatic' | 'Manual' | 'Semi-Automatic';\n vin?: string;\n currentRegistration?: {\n country?: string;\n };\n suspension?: 'Air' | 'Air/Steel' | 'Steel';\n constructionYear?: number;\n firstRegistration?: string;\n odometerKm?: number;\n powerHp?: number;\n bodystyle?: string;\n accessories: VehicleExternalInfo['accessories'];\n superstructure?: Pick<VehicleSuperstructure, 'crane' | 'tailgate'>;\n};\n\n// mostly inspired by the Hexon data model\nexport type VehicleExternalInfo = {\n body?: {\n cabin?: {\n type: 'Day' | 'Sleep / Small' | 'Sleep / Medium' | 'Sleep / Large';\n model?: string;\n };\n colour?: string;\n dimensions?: {\n height?: number;\n length?: number;\n width?: number;\n };\n doorCount?: number;\n interior?: {\n bedCount?: number;\n seatCount?: number;\n };\n internalDimensions?: {\n //for Trailers/Semi-trailers of type reefers, box, curtainsides\n height?: number;\n length?: number;\n width?: number;\n };\n kingpin?: {\n //for Trailers/Semi-trailers\n size?: number; // in inches\n capacity?: number; // in kg\n };\n fifthWheelHeight?: number; //for new trailers in mm\n };\n mainPicture?: AttachmentValue;\n pictures?: AttachmentValue[];\n videos?: AttachmentValue[];\n condition?: {\n damaged?: boolean;\n odometer?: {\n km?: number;\n };\n used?: boolean;\n };\n general: {\n bodystyle?: string;\n category: string;\n make?: string;\n model?: string;\n modelName?: string;\n type?: string;\n typeName?: string;\n tacho?: VehicleTachoTypes;\n color?: {\n mainColorHex?: string; //Hex color code\n chassisColorReferenceCode?: string; //Maker reference code. For now, mainly relevant for new trailers\n upperStructureColorReferenceCode?: string; //Maker reference code. For now, mainly relevant for new trailers\n };\n };\n history?: {\n advertisingYear?: number;\n constructionYear?: number;\n currentRegistration?: {\n country?: string;\n technicalInspectionEndDate?: string;\n carpassUrl?: string;\n carpassAvailable?: boolean;\n };\n firstRegistration?: string;\n cocPvg?: string;\n };\n identification: {\n licensePlate?: string;\n stockNumber?: string;\n vin?: string;\n buildStockNumber?: string;\n engineNumber?: string;\n };\n trailers?: {\n cooler?: {\n hasCooler?: boolean;\n make?: string;\n };\n };\n powertrain?: {\n axles?: VehicleAxlesInfo;\n emissions?: {\n class?: string;\n subclass?: string;\n };\n engine?: {\n cylinderCount?: number;\n energy?: { type?: VehicleFuelType };\n power?: {\n hp?: number;\n };\n displacement?: number;\n };\n transmission?: {\n gearCount?: number;\n make?: string;\n model?: string;\n type?: 'Automatic' | 'Manual' | 'Semi-Automatic';\n };\n };\n salesConditions: {\n leasing?: boolean;\n price?: string | undefined;\n currency?: string | undefined;\n /**\n * Read-only field maintained by `fillReadOnlyFields` on every vehicle save:\n * the value of `price` immediately before the most recent price change. Stays\n * unchanged when `price` does not change between saves. Same currency as\n * `price`. Useful for storefront \"was/now\" price-drop labels and for the AI\n * assistant's negotiation context.\n */\n previousPrice?: string;\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n included?: boolean;\n remarksIncluded?: string;\n };\n /**\n * Per-country snapshot computed by the owning tenant's `fillReadOnlyFields`.\n * Every field on this snapshot is public — `vehicle.external` rides through\n * to the storefront via cross-tenant sync, so internal-only values (e.g. the\n * owner's freight cost) must not appear here. Cross-tenant sync derives the\n * internal `externalTransport` figure directly from the owner's country-\n * delivery rule at sync time.\n *\n * Each entry exposes:\n * - `price` — bare currency conversion for the rule's country.\n * - `previousPrice` — bare conversion of `salesConditions.previousPrice`\n * when set.\n * - `deliveryPrice` — customer-facing delivery fee (when the rule covers\n * the vehicle's category). Storefront consumers sum\n * `price + (deliveryPrice ?? 0)` at the display layer.\n * - `deliveryLocation` — human-readable label, present whenever the rule\n * declared a location for this country.\n *\n * Only the original owner recomputes on save; cross-tenant copies preserve\n * the array.\n */\n localizedPricing?: Array<{\n countryCode: string;\n currency: string;\n price: number;\n previousPrice?: number;\n deliveryPrice?: number;\n deliveryLocation?: string;\n }>;\n };\n superstructure?: VehicleSuperstructure;\n weights?: {\n gvw?: number;\n massEmpty?: number;\n };\n accessories: {\n abs?: boolean;\n jumbo?: boolean;\n adr?: boolean;\n airco?: boolean;\n alloyWheels?: boolean;\n aluminiumFuelTank?: boolean;\n cdPlayer?: boolean;\n coil?: boolean;\n doubleStock?: boolean;\n centralLocking?: boolean;\n centralLubrication?: boolean;\n cruiseControl?: boolean;\n electricDoorMirrors?: boolean;\n electricWindows?: boolean;\n engineBrake?: boolean;\n forklift?: boolean;\n navigationSystem?: boolean;\n hydraulicTipperKit?: boolean;\n lowDeck?: boolean;\n lowNoise?: boolean;\n mega?: boolean;\n parkingHeater?: boolean;\n particleFilter?: boolean;\n powerTakeOff?: boolean;\n refrigerator?: boolean;\n retarderIntarder?: boolean;\n reversingCamera?: boolean;\n roofSpoiler?: boolean;\n sideSkirts?: boolean;\n spareKey?: boolean;\n spareWheel?: boolean;\n speedLimiter?: boolean;\n spoilers?: boolean;\n spotlights?: boolean;\n stabilityControl?: boolean;\n standardAirco?: boolean;\n television?: boolean;\n toolbox?: boolean;\n trailerCoupling?: boolean;\n twinFuelTank?: boolean;\n visor?: boolean;\n xenonLights?: boolean;\n };\n publicLink?: string;\n};\n\ntype BaseVehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = {\n internal: I;\n external: E;\n original?: VehicleOriginalInfo;\n};\n\nexport type NewVehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = BaseVehicle<I, E> & {\n id?: undefined;\n};\n\nexport type Vehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = BaseVehicle<I, E> & {\n id: string;\n};\n\nexport type VehicleBffMetadata = {\n openOpportunities: number;\n};\n\nexport type VehicleBff = {\n vehicle: DataRecord<Vehicle, VehicleBffMetadata>;\n sale?: VehicleSaleInfo;\n purchase?: VehiclePurchase;\n overview?: VehicleBusinessOverviewXL;\n marketing?: {\n titles?: MultiLangValue;\n listed: boolean;\n sharable: boolean;\n publishedOnWebsiteAt?: string;\n numPlatformsPublished: number;\n };\n};\n\nexport type VehiclesByChassisNumberRequest = {\n tenantIds: string[];\n chassisNumber: string;\n};\n\nexport type VehiclesByMetaIdRequest = {\n tenantIds: string[];\n metaId: string;\n};\n\nexport type VehicleAxle = {\n make?: string;\n alloyWheels?: boolean;\n brakes?: 'Disc' | 'Drum';\n differentialLock?: boolean;\n liftAxle?: boolean;\n maxLoad?: number;\n nr: number;\n position?: string;\n powered?: boolean;\n reduction?: string;\n steering?: boolean;\n suspension?: 'Steel' | 'Air' | 'Hydraulic';\n twinWheels?: boolean;\n tyreSize?: string;\n tyreConditionType?: 'mm' | '%';\n\n tyreConditionMmLeftInside?: number;\n tyreConditionMmLeftOutside?: number;\n tyreConditionMmRightInside?: number;\n tyreConditionMmRightOutside?: number;\n\n tyreConditionPercLeftInside?: number;\n tyreConditionPercLeftOutside?: number;\n tyreConditionPercRightInside?: number;\n tyreConditionPercRightOutside?: number;\n\n steeringType?: 'fixed' | 'self_steering' | 'hydraulic';\n};\n\nexport enum VehiclePurchaseDealStatus {\n Marketing = 'MARKETING',\n PurchaseEvaluation = 'PURCHASE_EVALUATION', // used for vehicle consignation\n PurchaseAgreed = 'PURCHASE_AGREED',\n FirstPaymentAllowed = 'FIRST_PAYMENT_ALLOWED',\n FirstPaymentSent = 'FIRST_PAYMENT_SENT',\n FullPaymentAllowed = 'FULL_PAYMENT_ALLOWED',\n FullPaymentSent = 'FULL_PAYMENT_SENT',\n}\n\nexport enum VehiclePurchaseLogisticsStatus {\n AtOrigin = 'AT_ORIGIN',\n Arrived = 'ARRIVED',\n CheckedIn = 'CHECKED_IN',\n NotApplicable = 'NOT_APPLICABLE',\n}\n\nexport enum VehiclePurchaseDeliveryType {\n PickUp = 'PICK_UP',\n DropOff = 'DROP_OFF',\n}\n\nexport type VehicleStatus = {\n deleted?: boolean;\n deletedReason?: string;\n};\n\nexport type VehiclePriceList = {\n uploadedAt: string;\n uploadedBy: string;\n priceList: AttachmentValue;\n id: string;\n};\n\nexport type UpdateVehiclesPricesRequest = {\n priceList: AttachmentValue;\n};\n\nexport type VehiclePriceListLine = {\n reference: string;\n internet?: string;\n premium?: string;\n minimum?: string;\n};\n\nexport type VehiclePriceUpdateError = {\n data: VehiclePriceListLine;\n line: number;\n error: {\n fieldId: keyof VehiclePriceListLine;\n msg: string;\n };\n};\n\nexport type VehiclePriceUpdateResponse =\n | {\n success: true;\n }\n | {\n success: false;\n errors: VehiclePriceUpdateError[];\n };\n\nexport type VehiclePurchase = {\n organizationId: string;\n contactId: string;\n supplierStockNumber?: string;\n purchasedBy: string;\n purchaseAssistedBy?: string;\n\n log: {\n // computed automatically by the backend\n marketingStartedAt?: string;\n marketingStartedBy?: string;\n\n evaluationStartedAt?: string;\n evaluationStartedBy?: string;\n\n purchaseAgreedAt?: string;\n purchaseAgreedBy?: string;\n\n firstPaymentAllowedAt?: string;\n firstPaymentAllowedBy?: string;\n firstPaymentSentAt?: string;\n firstPaymentSentBy?: string;\n\n fullPaymentAllowedAt?: string;\n fullPaymentAllowedBy?: string;\n fullPaymentSentAt?: string;\n fullPaymentSentBy?: string;\n\n arrivedAt?: string;\n arrivedBy?: string;\n\n checkedInAt?: string;\n checkedInBy?: string;\n };\n\n deal: {\n status: VehiclePurchaseDealStatus; // partially set by the user, if there are payments then it is set automatically by the backend\n readyToDeliver?: boolean;\n\n purchaseVatRate?: string;\n purchase?: string; // exclusive VAT\n\n bonus?: string; // sometimes manufacturers pay us a bonus when we reach a target\n\n // estimated costs, real costs come from actual invoices\n commission?: string;\n externalTransport?: string;\n internalTransport?: string;\n externalGarage?: string;\n internalGarage?: string;\n other?: string;\n\n totalCosts?: string; // exclusive VAT\n };\n\n logistics: {\n parkingName?: string;\n status: VehiclePurchaseLogisticsStatus; // manually set by the user\n deliveryType?: VehiclePurchaseDeliveryType;\n pickupAddress?: LocationValue;\n availableWeek?: string; // ISOWEEK, e.g. 2016-W02\n };\n};\n\nexport type PurchaseRecordMeta = ReferenceData & {\n allowedLogisticsTransitions?: VehiclePurchaseLogisticsStatus[];\n};\n\nexport type SaveVehiclePurchaseOpts = {\n skipTransportValidation?: boolean;\n};\n\nexport enum VehicleActions {\n EditAdminDocs = 'edit_admin_docs',\n EditEngineeringDocs = 'edit_engineering_docs',\n EditMarketing = 'edit_marketing',\n EditMedia = 'edit_media',\n EditPictures = 'edit_pictures',\n EditPrices = 'edit_prices',\n EditPurchaseDeal = 'edit_purchase_deal',\n EditPurchaseLogistics = 'edit_purchase_logistics',\n EditSpecs = 'edit_specs',\n EditVideos = 'edit_videos',\n ViewLeads = 'view_leads',\n ViewBusinessOverview = 'view_business_overview',\n ViewSimilarOnline = 'view_similar_online',\n ViewPurchase = 'view_purchase',\n ViewPriceHistory = 'view_price_history',\n DeleteVehicle = 'delete_vehicle',\n}\n\nexport enum PurchaseActions {\n ViewDeal = 'view_deal',\n ViewSupplierOrganization = 'view_supplier_organization',\n ViewContact = 'view_contact',\n ViewLegalAddress = 'view_legal_address',\n ViewPickupAddress = 'view_pickup_address',\n ViewEstimatedCosts = 'view_estimated_costs',\n ViewFinanceDocuments = 'view_finance_documents',\n AddPayment = 'add_payment',\n EditPayment = 'edit_payment',\n MarkPaymentDone = 'mark_payment_done',\n AddRemarks = 'add_remarks',\n}\n\nexport enum VehicleBusinessOverviewActions {\n View = 'view_business_overview',\n}\n\nexport type VehicleShareEmailRequest = ShareEmailRequest & {\n pictures: AttachmentValue[];\n};\n\nexport type VehicleShareData = {\n title: string;\n mainPicture?: string;\n pictures: {\n picture: string;\n }[];\n message: string;\n specs: {\n name: string;\n value: string;\n }[];\n features: {\n name: string;\n }[];\n stockNumber: string;\n price?: string;\n description?: string;\n publicLink?: string;\n};\n\nexport type MarketingEmailData = {\n message: string;\n vehicles: VehicleShareData[];\n vehiclePairs: { first: VehicleShareData; second?: VehicleShareData }[];\n lastVehicle?: VehicleShareData;\n unsubscribeUrl?: string;\n};\n\nexport type VehicleReservationCreateEvent = BaseVehicleEvent & {\n action: 'create';\n reservation: OpportunityReservation;\n previousReservation?: undefined;\n};\n\nexport type VehicleReservationUpdateEvent = BaseVehicleEvent & {\n action: 'update';\n reservation: OpportunityReservation;\n previousReservation: OpportunityReservation;\n};\n\nexport type VehicleReservationDeleteEvent = BaseVehicleEvent & {\n action: 'delete';\n reservation?: undefined;\n previousReservation: OpportunityReservation;\n};\n\nexport type VehicleReservationEvent =\n | VehicleReservationCreateEvent\n | VehicleReservationUpdateEvent\n | VehicleReservationDeleteEvent;\n\nexport type ShareVehicleViaWahtsappRequest = {\n data: ShareWhatsappRequest;\n vehicleId: string;\n};\n\ntype RecordChange = {\n modifiedAt: string;\n modifiedBy: string;\n};\n\nexport type VehicleChange = RecordChange & {\n vehicle: Vehicle;\n};\n\nexport type PurchaseChange = RecordChange & {\n purchase: VehiclePurchase;\n};\n\nexport type RecordChangeRequest = PaginatedRequest & {\n fieldPath?: string;\n};\n"]}
1
+ {"version":3,"file":"vehicles.js","sourceRoot":"/","sources":["types/vehicles.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n AttachmentValue,\n Document,\n LocationValue,\n MultiLangValue,\n PaginatedRequest,\n SearchPermissions,\n ShareEmailRequest,\n ShareWhatsappRequest,\n} from '@bisondesk/commons-sdk/types';\nimport { ReferenceData } from '@bisondesk/commons-sdk/utils';\nimport { BaseEvent } from './internal-events.js';\nimport { VehicleSaleLogisticsStatus } from './opportunities.js';\nimport { OpportunityReservation } from './reservations.js';\nimport { ComplexFilter, Filter } from './search.js';\nimport { Transport } from './transports.js';\nimport { DataRecord } from './utils.js';\nimport { VehicleBusinessOverviewXL } from './vehicle-performance.js';\nimport { VehicleSaleInfo } from './vehicle-sales.js';\n\nexport enum VehicleRoles {\n SALE = 'SALE',\n OWN_USE = 'OWN_USE',\n RENTAL = 'RENTAL',\n BUILD = 'BUILD',\n UNKNOWN = 'UNKNOWN',\n BANK_FINANCE = 'BANK_FINANCE',\n}\n\nexport enum VehicleFuelType {\n Petrol = 'PETROL',\n Diesel = 'DIESEL',\n LPG = 'LPG',\n LPG_G3 = 'LPG_G3',\n Electric = 'ELECTRIC',\n CNG = 'CNG',\n Hydrogen = 'HYDROGEN',\n BioEthanol = 'BIO_ETHANOL',\n BioDiesel = 'BIO_DIESEL',\n E85 = 'E85',\n HCNG = 'HCNG',\n LPG_Petrol = 'LPG_PETROL',\n LPG_Diesel = 'LPG_DIESEL',\n LPG_G3_Petrol = 'LPG_G3_PETROL',\n LPG_G3_Diesel = 'LPG_G3_DIESEL',\n Electric_Diesel = 'ELECTRIC_DIESEL',\n Electric_Petrol = 'ELECTRIC_PETROL',\n Hybrid = 'HYBRID',\n Other = 'OTHER',\n}\n\nexport enum VehicleTachoTypes {\n Analog = 'ANALOG',\n Digital = 'DIGITAL',\n SmartTacho1 = 'SMART_TACHO_1',\n SmartTacho2 = 'SMART_TACHO_2',\n}\n\ntype BaseVehicleEvent = BaseEvent & {\n id: string;\n actionAt: string;\n userId: string;\n tenantId: string;\n};\n\ntype VehicleSuperstructure = {\n carCapacity?: number;\n compartmentCount?: number;\n counter?: boolean;\n crane?: {\n make?: string;\n model?: string;\n position?: 'Behind the cabin' | 'Rear';\n present?: boolean;\n year?: number;\n };\n dimensions?: {\n height?: number;\n length?: number;\n width?: number;\n extended?: string; // L1, L2, ...\n heightened?: string; // H1, H2, ...\n };\n baseVehicle?: SuperstructureBaseVehicle;\n extendable?: boolean;\n highPressurePump?: boolean;\n loadingPlatformHeight?: number;\n pump?: boolean;\n slidingRoof?: boolean;\n tailgate?: {\n capacity?: number;\n make?: string;\n model?: string;\n present?: boolean;\n type?: 'slider_lift' | 'cantilever' | 'column_lift';\n };\n tank?: {\n capacity: number; //litters\n volume?: number; //m3\n };\n temperatureControl?: {\n engineType?: 'diesel' | 'electric' | 'diesel+electric' | 'engine';\n make?: string;\n runningHours?: {\n diesel?: number;\n electric?: number;\n };\n };\n tipper?: {\n tipsBack?: boolean;\n tipsLeft?: boolean;\n tipsRight?: boolean;\n };\n};\n\nexport type NewVehicleRequest = {\n branch: string;\n count: number;\n specs: VehicleExternalInfo;\n description: {\n remarks?: string;\n technicalRemarks?: string;\n };\n salesConditions?: {\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n };\n };\n logistics: {\n parking?: string;\n deliveryType?: VehiclePurchaseDeliveryType;\n pickupAddress?: LocationValue;\n status: VehiclePurchaseLogisticsStatus;\n };\n prices: {\n minimum?: string;\n premium?: string;\n internet?: string;\n };\n media: {\n internalPictures: AttachmentValue[];\n pictures: AttachmentValue[];\n videos: AttachmentValue[];\n docs: Document[];\n engineeringDocs: Document[];\n };\n vinList?: string;\n deal: {\n offerId?: string;\n stockNumber?: string;\n contactId: string;\n orgId: string;\n purchasedBy: string;\n purchaseAssistedBy?: string;\n readyToDeliver?: boolean;\n dealStatus: VehiclePurchaseDealStatus;\n costs: {\n purchase: string;\n purchaseVatPercentage: string;\n commission?: string;\n externalTransport?: string;\n internalTransport?: string;\n externalGarage?: string;\n internalGarage?: string;\n other?: string;\n bonus?: string;\n };\n logistics: {\n availableDate?: string;\n vehicleRole: VehicleRoles;\n };\n };\n};\n\nexport type VehicleCreateEvent = BaseVehicleEvent & {\n action: 'create';\n vehicle: Vehicle;\n previousVehicle?: undefined;\n};\n\nexport type VehicleUpdateEvent = BaseVehicleEvent & {\n action: 'update';\n vehicle: Vehicle;\n previousVehicle: Vehicle;\n};\n\nexport type VehicleDeleteEvent = BaseVehicleEvent & {\n action: 'delete';\n vehicle?: undefined;\n previousVehicle: Vehicle;\n};\n\nexport type VehicleEvent = VehicleCreateEvent | VehicleUpdateEvent | VehicleDeleteEvent;\n\ntype BaseVehiclePurchaseEvent = {\n actionAt: string;\n userId: string;\n tenantId: string;\n branchId: string;\n vehicleId: string;\n};\n\nexport type VehiclePurchaseCreateEvent = BaseVehiclePurchaseEvent & {\n action: 'create';\n data: VehiclePurchase;\n previousData?: undefined;\n};\n\nexport type VehiclePurchaseUpdateEvent = BaseVehiclePurchaseEvent & {\n action: 'update';\n data: VehiclePurchase;\n previousData: VehiclePurchase;\n};\n\nexport type VehiclePurchaseEvent = BaseEvent &\n (VehiclePurchaseCreateEvent | VehiclePurchaseUpdateEvent);\n\nexport type UpdateVehiclesPricesEvent = {\n id: string;\n actionAt: string;\n userId: string;\n tenantId: string;\n priceList: AttachmentValue;\n};\n\nexport type PublicVehicle = Pick<Vehicle, 'external' | 'id'>;\n\nexport type PublicSearchVehicle = {\n vehicle: PublicVehicle;\n};\n\nexport enum SearchVehicleSaleDealStatus {\n NotSold = 'NOT_SOLD',\n Reserved = 'RESERVED',\n Sold = 'SOLD',\n}\n\nexport type SearchVehicle = {\n custom: {\n features: string[];\n sold: boolean;\n isReserved: boolean;\n hasPayments: boolean;\n listedOnWebsite: boolean;\n publishedOnWebsiteAt?: string;\n originalBranchId: string;\n };\n customSortableFields: {\n priceChangedAt?: string;\n checkedInAt?: string;\n saleAgreementAt?: string;\n /** Date the vehicle entered virtual inventory (marketing ?? evaluation ?? purchase-agreed). */\n virtualInventoryStartedAt?: string;\n };\n purchase?: {\n supplierStockNumber?: string;\n readyToDeliver: boolean;\n dealStatus?: VehiclePurchaseDealStatus;\n logisticsStatus?: VehiclePurchaseLogisticsStatus;\n purchasedBy?: string;\n purchaseAgreedAt?: string;\n parkingName?: string;\n organizationId: string;\n organizationExternalId?: number;\n fullPaymentSentAt?: string;\n marketingStartedAt?: string;\n arrivedAt?: string;\n };\n reservation?: Pick<OpportunityReservation, 'expiresAt' | 'opportunityId' | 'createdBy'>;\n transports: Transport[];\n vehicle: Vehicle;\n sale: {\n opportunityId?: string;\n externalSaleId?: string;\n soldAt?: string;\n soldBy?: string;\n sellers?: string[]; // opportunity account manager [+ assistant]\n dealStatus: SearchVehicleSaleDealStatus;\n logisticStatus?: VehicleSaleLogisticsStatus;\n };\n};\n\nexport type InputSearchVehicle = SearchVehicle & {\n permissions?: SearchPermissions;\n};\n\nexport type VehicleSearchMeta = ReferenceData & {\n matchingOffers?: {\n count: number;\n filters: {\n conditions: Array<Filter | ComplexFilter>;\n fullText?: string;\n };\n };\n};\n\nexport type PriceChangeEvent = BaseEvent & {\n tenantId: string;\n userId: string;\n vehicleId: string;\n input: {\n previousPrices: VehiclePricingHistoryInput;\n currentPrices: VehiclePricingHistoryInput;\n };\n};\n\nexport type VehiclePricingHistory = {\n pricing: VehiclePricingHistoryInput;\n createdAt: string;\n createdBy: string;\n};\n\nexport type VehiclePricingHistoryInput = VehiclePricing & { internet?: string };\n\nexport type VehiclePricing = {\n premium?: string;\n minimum?: string;\n currency?: string;\n};\n\nexport type RelatedVehicle = {\n vehicleId: string;\n description: string;\n stockNumber: string;\n};\n\nexport type VehicleInternalInfo = {\n createdAt: string;\n description: {\n title: string;\n remarks?: string;\n technicalRemarks?: string;\n };\n identification: {\n administrativeNumber?: string;\n stockNumber: string;\n branch: string;\n customerStockNumber?: string;\n offerId?: string;\n licensePlateHistory?: {\n licensePlate: string;\n changedAt: string;\n }[];\n\n /**\n * This ID is used to link different vehicle objects that represent the same real world asset.\n * Ideally the chassis number could be used, but in some cases it is not available yet.\n */\n metaId: string;\n };\n documents: Document[];\n pictures: AttachmentValue[];\n engineeringDocs: Document[];\n pricing: VehiclePricing;\n role: VehicleRoles;\n status: VehicleStatus;\n updatedAt: string;\n /* use this property to store additional terms that you want the vehicle to be found with when doing a full text search */\n keywords?: string[];\n relatedVehicles: RelatedVehicle[];\n};\n\nexport type VehicleOriginalInfo = {\n /** IMMEDIATE source tenant (the predecessor in a sync chain). */\n tenantId: string;\n tenantChain?: string[];\n identification: {\n id: string;\n branchId: string;\n stockNumber: string;\n metaId?: string;\n country: string;\n };\n salesConditions: {\n leasing?: boolean;\n price?: string;\n currency?: string;\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n };\n /**\n * The immediate source's sellability at the last sync hop: true when the source was not\n * sellable — not in `SALE` role, had an active reservation or external sale, or was itself\n * unavailable. Derived (never set by a human) and refreshed on every cross-tenant update so\n * it cascades down the whole chain.\n */\n unavailable?: boolean;\n };\n /**\n * Deepest-origin tenant/branch in a multi-hop sync chain. Carries forward\n * verbatim across hops so consumers can answer \"where did this vehicle\n * actually originate?\" without walking the chain.\n */\n owner: {\n tenantId: string;\n branchId: string;\n country: string;\n };\n};\n\nexport type VehicleAxlesInfo = {\n details?: VehicleAxle[];\n configuration?: string;\n count?: number;\n poweredAxlesCount?: number;\n wheelbase?: number;\n suspension?: 'Air' | 'Air/Steel' | 'Steel';\n loadCapacity?: number;\n};\n\nexport type SuperstructureBaseVehicle = {\n category: string;\n make: string;\n model?: string;\n type?: string;\n axlesConfiguration?: string;\n emissionsClass?: string;\n transmissionType?: 'Automatic' | 'Manual' | 'Semi-Automatic';\n vin?: string;\n currentRegistration?: {\n country?: string;\n };\n suspension?: 'Air' | 'Air/Steel' | 'Steel';\n constructionYear?: number;\n firstRegistration?: string;\n odometerKm?: number;\n powerHp?: number;\n bodystyle?: string;\n accessories: VehicleExternalInfo['accessories'];\n superstructure?: Pick<VehicleSuperstructure, 'crane' | 'tailgate'>;\n};\n\n// mostly inspired by the Hexon data model\nexport type VehicleExternalInfo = {\n body?: {\n cabin?: {\n type: 'Day' | 'Sleep / Small' | 'Sleep / Medium' | 'Sleep / Large';\n model?: string;\n };\n colour?: string;\n dimensions?: {\n height?: number;\n length?: number;\n width?: number;\n };\n doorCount?: number;\n interior?: {\n bedCount?: number;\n seatCount?: number;\n };\n internalDimensions?: {\n //for Trailers/Semi-trailers of type reefers, box, curtainsides\n height?: number;\n length?: number;\n width?: number;\n };\n kingpin?: {\n //for Trailers/Semi-trailers\n size?: number; // in inches\n capacity?: number; // in kg\n };\n fifthWheelHeight?: number; //for new trailers in mm\n };\n mainPicture?: AttachmentValue;\n pictures?: AttachmentValue[];\n videos?: AttachmentValue[];\n condition?: {\n damaged?: boolean;\n odometer?: {\n km?: number;\n };\n used?: boolean;\n };\n general: {\n bodystyle?: string;\n category: string;\n make?: string;\n model?: string;\n modelName?: string;\n type?: string;\n typeName?: string;\n tacho?: VehicleTachoTypes;\n color?: {\n mainColorHex?: string; //Hex color code\n chassisColorReferenceCode?: string; //Maker reference code. For now, mainly relevant for new trailers\n upperStructureColorReferenceCode?: string; //Maker reference code. For now, mainly relevant for new trailers\n };\n };\n history?: {\n advertisingYear?: number;\n constructionYear?: number;\n currentRegistration?: {\n country?: string;\n technicalInspectionEndDate?: string;\n carpassUrl?: string;\n carpassAvailable?: boolean;\n };\n firstRegistration?: string;\n cocPvg?: string;\n };\n identification: {\n licensePlate?: string;\n stockNumber?: string;\n vin?: string;\n buildStockNumber?: string;\n engineNumber?: string;\n };\n trailers?: {\n cooler?: {\n hasCooler?: boolean;\n make?: string;\n };\n };\n powertrain?: {\n axles?: VehicleAxlesInfo;\n emissions?: {\n class?: string;\n subclass?: string;\n };\n engine?: {\n cylinderCount?: number;\n energy?: { type?: VehicleFuelType };\n power?: {\n hp?: number;\n };\n displacement?: number;\n };\n transmission?: {\n gearCount?: number;\n make?: string;\n model?: string;\n type?: 'Automatic' | 'Manual' | 'Semi-Automatic';\n };\n };\n salesConditions: {\n leasing?: boolean;\n price?: string | undefined;\n currency?: string | undefined;\n /**\n * Read-only field maintained by `fillReadOnlyFields` on every vehicle save:\n * the value of `price` immediately before the most recent price change. Stays\n * unchanged when `price` does not change between saves. Same currency as\n * `price`. Useful for storefront \"was/now\" price-drop labels and for the AI\n * assistant's negotiation context.\n */\n previousPrice?: string;\n marginScheme?: boolean;\n manufacturerWarranty?: {\n remarks?: string;\n available?: boolean;\n included?: boolean;\n remarksIncluded?: string;\n };\n /**\n * Per-country snapshot computed by the owning tenant's `fillReadOnlyFields`.\n * Every field on this snapshot is public — `vehicle.external` rides through\n * to the storefront via cross-tenant sync, so internal-only values (e.g. the\n * owner's freight cost) must not appear here. Cross-tenant sync derives the\n * internal `externalTransport` figure directly from the owner's country-\n * delivery rule at sync time.\n *\n * Each entry exposes:\n * - `price` — bare currency conversion for the rule's country.\n * - `previousPrice` — bare conversion of `salesConditions.previousPrice`\n * when set.\n * - `deliveryPrice` — customer-facing delivery fee (when the rule covers\n * the vehicle's category). Storefront consumers sum\n * `price + (deliveryPrice ?? 0)` at the display layer.\n * - `deliveryLocation` — human-readable label, present whenever the rule\n * declared a location for this country.\n *\n * Only the original owner recomputes on save; cross-tenant copies preserve\n * the array.\n */\n localizedPricing?: Array<{\n countryCode: string;\n currency: string;\n price: number;\n previousPrice?: number;\n deliveryPrice?: number;\n deliveryLocation?: string;\n }>;\n };\n superstructure?: VehicleSuperstructure;\n weights?: {\n gvw?: number;\n massEmpty?: number;\n };\n accessories: {\n abs?: boolean;\n jumbo?: boolean;\n adr?: boolean;\n airco?: boolean;\n alloyWheels?: boolean;\n aluminiumFuelTank?: boolean;\n cdPlayer?: boolean;\n coil?: boolean;\n doubleStock?: boolean;\n centralLocking?: boolean;\n centralLubrication?: boolean;\n cruiseControl?: boolean;\n electricDoorMirrors?: boolean;\n electricWindows?: boolean;\n engineBrake?: boolean;\n forklift?: boolean;\n navigationSystem?: boolean;\n hydraulicTipperKit?: boolean;\n lowDeck?: boolean;\n lowNoise?: boolean;\n mega?: boolean;\n parkingHeater?: boolean;\n particleFilter?: boolean;\n powerTakeOff?: boolean;\n refrigerator?: boolean;\n retarderIntarder?: boolean;\n reversingCamera?: boolean;\n roofSpoiler?: boolean;\n sideSkirts?: boolean;\n spareKey?: boolean;\n spareWheel?: boolean;\n speedLimiter?: boolean;\n spoilers?: boolean;\n spotlights?: boolean;\n stabilityControl?: boolean;\n standardAirco?: boolean;\n television?: boolean;\n toolbox?: boolean;\n trailerCoupling?: boolean;\n twinFuelTank?: boolean;\n visor?: boolean;\n xenonLights?: boolean;\n };\n publicLink?: string;\n};\n\ntype BaseVehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = {\n internal: I;\n external: E;\n original?: VehicleOriginalInfo;\n};\n\nexport type NewVehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = BaseVehicle<I, E> & {\n id?: undefined;\n};\n\nexport type Vehicle<I = VehicleInternalInfo, E = VehicleExternalInfo> = BaseVehicle<I, E> & {\n id: string;\n};\n\nexport type VehicleBffMetadata = {\n openOpportunities: number;\n};\n\nexport type VehicleBff = {\n vehicle: DataRecord<Vehicle, VehicleBffMetadata>;\n sale?: VehicleSaleInfo;\n purchase?: VehiclePurchase;\n overview?: VehicleBusinessOverviewXL;\n marketing?: {\n titles?: MultiLangValue;\n listed: boolean;\n sharable: boolean;\n publishedOnWebsiteAt?: string;\n numPlatformsPublished: number;\n };\n};\n\nexport type VehiclesByChassisNumberRequest = {\n tenantIds: string[];\n chassisNumber: string;\n};\n\nexport type VehiclesByMetaIdRequest = {\n tenantIds: string[];\n metaId: string;\n};\n\nexport type VehicleAxle = {\n make?: string;\n alloyWheels?: boolean;\n brakes?: 'Disc' | 'Drum';\n differentialLock?: boolean;\n liftAxle?: boolean;\n maxLoad?: number;\n nr: number;\n position?: string;\n powered?: boolean;\n reduction?: string;\n steering?: boolean;\n suspension?: 'Steel' | 'Air' | 'Hydraulic';\n twinWheels?: boolean;\n tyreSize?: string;\n tyreConditionType?: 'mm' | '%';\n\n tyreConditionMmLeftInside?: number;\n tyreConditionMmLeftOutside?: number;\n tyreConditionMmRightInside?: number;\n tyreConditionMmRightOutside?: number;\n\n tyreConditionPercLeftInside?: number;\n tyreConditionPercLeftOutside?: number;\n tyreConditionPercRightInside?: number;\n tyreConditionPercRightOutside?: number;\n\n steeringType?: 'fixed' | 'self_steering' | 'hydraulic';\n};\n\nexport enum VehiclePurchaseDealStatus {\n Marketing = 'MARKETING',\n PurchaseEvaluation = 'PURCHASE_EVALUATION', // used for vehicle consignation\n PurchaseAgreed = 'PURCHASE_AGREED',\n FirstPaymentAllowed = 'FIRST_PAYMENT_ALLOWED',\n FirstPaymentSent = 'FIRST_PAYMENT_SENT',\n FullPaymentAllowed = 'FULL_PAYMENT_ALLOWED',\n FullPaymentSent = 'FULL_PAYMENT_SENT',\n}\n\nexport enum VehiclePurchaseLogisticsStatus {\n AtOrigin = 'AT_ORIGIN',\n Arrived = 'ARRIVED',\n CheckedIn = 'CHECKED_IN',\n NotApplicable = 'NOT_APPLICABLE',\n}\n\nexport enum VehiclePurchaseDeliveryType {\n PickUp = 'PICK_UP',\n DropOff = 'DROP_OFF',\n}\n\nexport type VehicleStatus = {\n deleted?: boolean;\n deletedReason?: string;\n};\n\nexport type VehiclePriceList = {\n uploadedAt: string;\n uploadedBy: string;\n priceList: AttachmentValue;\n id: string;\n};\n\nexport type UpdateVehiclesPricesRequest = {\n priceList: AttachmentValue;\n};\n\nexport type VehiclePriceListLine = {\n reference: string;\n internet?: string;\n premium?: string;\n minimum?: string;\n};\n\nexport type VehiclePriceUpdateError = {\n data: VehiclePriceListLine;\n line: number;\n error: {\n fieldId: keyof VehiclePriceListLine;\n msg: string;\n };\n};\n\nexport type VehiclePriceUpdateResponse =\n | {\n success: true;\n }\n | {\n success: false;\n errors: VehiclePriceUpdateError[];\n };\n\nexport type VehiclePurchase = {\n organizationId: string;\n contactId: string;\n supplierStockNumber?: string;\n purchasedBy: string;\n purchaseAssistedBy?: string;\n\n log: {\n // computed automatically by the backend\n marketingStartedAt?: string;\n marketingStartedBy?: string;\n\n evaluationStartedAt?: string;\n evaluationStartedBy?: string;\n\n purchaseAgreedAt?: string;\n purchaseAgreedBy?: string;\n\n firstPaymentAllowedAt?: string;\n firstPaymentAllowedBy?: string;\n firstPaymentSentAt?: string;\n firstPaymentSentBy?: string;\n\n fullPaymentAllowedAt?: string;\n fullPaymentAllowedBy?: string;\n fullPaymentSentAt?: string;\n fullPaymentSentBy?: string;\n\n arrivedAt?: string;\n arrivedBy?: string;\n\n checkedInAt?: string;\n checkedInBy?: string;\n };\n\n deal: {\n status: VehiclePurchaseDealStatus; // partially set by the user, if there are payments then it is set automatically by the backend\n readyToDeliver?: boolean;\n\n purchaseVatRate?: string;\n purchase?: string; // exclusive VAT\n\n bonus?: string; // sometimes manufacturers pay us a bonus when we reach a target\n\n // estimated costs, real costs come from actual invoices\n commission?: string;\n externalTransport?: string;\n internalTransport?: string;\n externalGarage?: string;\n internalGarage?: string;\n other?: string;\n\n totalCosts?: string; // exclusive VAT\n };\n\n logistics: {\n parkingName?: string;\n status: VehiclePurchaseLogisticsStatus; // manually set by the user\n deliveryType?: VehiclePurchaseDeliveryType;\n pickupAddress?: LocationValue;\n availableWeek?: string; // ISOWEEK, e.g. 2016-W02\n };\n};\n\nexport type PurchaseRecordMeta = ReferenceData & {\n allowedLogisticsTransitions?: VehiclePurchaseLogisticsStatus[];\n};\n\nexport type SaveVehiclePurchaseOpts = {\n skipTransportValidation?: boolean;\n};\n\nexport enum VehicleActions {\n EditAdminDocs = 'edit_admin_docs',\n EditEngineeringDocs = 'edit_engineering_docs',\n EditMarketing = 'edit_marketing',\n EditMedia = 'edit_media',\n EditPictures = 'edit_pictures',\n EditPrices = 'edit_prices',\n EditPurchaseDeal = 'edit_purchase_deal',\n EditPurchaseLogistics = 'edit_purchase_logistics',\n EditSpecs = 'edit_specs',\n EditVideos = 'edit_videos',\n ViewLeads = 'view_leads',\n ViewBusinessOverview = 'view_business_overview',\n ViewSimilarOnline = 'view_similar_online',\n ViewPurchase = 'view_purchase',\n ViewPriceHistory = 'view_price_history',\n DeleteVehicle = 'delete_vehicle',\n}\n\nexport enum PurchaseActions {\n ViewDeal = 'view_deal',\n ViewSupplierOrganization = 'view_supplier_organization',\n ViewContact = 'view_contact',\n ViewLegalAddress = 'view_legal_address',\n ViewPickupAddress = 'view_pickup_address',\n ViewEstimatedCosts = 'view_estimated_costs',\n ViewFinanceDocuments = 'view_finance_documents',\n AddPayment = 'add_payment',\n EditPayment = 'edit_payment',\n MarkPaymentDone = 'mark_payment_done',\n AddRemarks = 'add_remarks',\n}\n\nexport enum VehicleBusinessOverviewActions {\n View = 'view_business_overview',\n}\n\nexport type VehicleShareEmailRequest = ShareEmailRequest & {\n pictures: AttachmentValue[];\n};\n\nexport type VehicleShareData = {\n title: string;\n mainPicture?: string;\n pictures: {\n picture: string;\n }[];\n message: string;\n specs: {\n name: string;\n value: string;\n }[];\n features: {\n name: string;\n }[];\n stockNumber: string;\n price?: string;\n description?: string;\n publicLink?: string;\n};\n\nexport type MarketingEmailData = {\n message: string;\n vehicles: VehicleShareData[];\n vehiclePairs: { first: VehicleShareData; second?: VehicleShareData }[];\n lastVehicle?: VehicleShareData;\n unsubscribeUrl?: string;\n};\n\nexport type VehicleReservationCreateEvent = BaseVehicleEvent & {\n action: 'create';\n reservation: OpportunityReservation;\n previousReservation?: undefined;\n};\n\nexport type VehicleReservationUpdateEvent = BaseVehicleEvent & {\n action: 'update';\n reservation: OpportunityReservation;\n previousReservation: OpportunityReservation;\n};\n\nexport type VehicleReservationDeleteEvent = BaseVehicleEvent & {\n action: 'delete';\n reservation?: undefined;\n previousReservation: OpportunityReservation;\n};\n\nexport type VehicleReservationEvent =\n | VehicleReservationCreateEvent\n | VehicleReservationUpdateEvent\n | VehicleReservationDeleteEvent;\n\nexport type ShareVehicleViaWahtsappRequest = {\n data: ShareWhatsappRequest;\n vehicleId: string;\n};\n\ntype RecordChange = {\n modifiedAt: string;\n modifiedBy: string;\n};\n\nexport type VehicleChange = RecordChange & {\n vehicle: Vehicle;\n};\n\nexport type PurchaseChange = RecordChange & {\n purchase: VehiclePurchase;\n};\n\nexport type RecordChangeRequest = PaginatedRequest & {\n fieldPath?: string;\n};\n"]}
@@ -0,0 +1,6 @@
1
+ import { AccountingProvider, Branch, Tenant } from '../types/tenants.js';
2
+ export declare const branchProvider: (branch: Branch | undefined) => AccountingProvider | undefined;
3
+ export declare const branchHasAccounting: (branch: Branch | undefined) => boolean;
4
+ export declare const tenantHasAccounting: (tenant: Tenant | undefined) => boolean;
5
+ export declare const branchHasAccountingInTenant: (tenant: Tenant | undefined, branchId: string) => boolean;
6
+ //# sourceMappingURL=accounting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounting.d.ts","sourceRoot":"/","sources":["utils/accounting.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAEzE,eAAO,MAAM,cAAc,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,kBAAkB,GAAG,SAQhF,CAAC;AAEF,eAAO,MAAM,mBAAmB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,OACjC,CAAC;AAEjC,eAAO,MAAM,mBAAmB,GAAI,QAAQ,MAAM,GAAG,SAAS,KAAG,OAQhE,CAAC;AAQF,eAAO,MAAM,2BAA2B,GACtC,QAAQ,MAAM,GAAG,SAAS,EAC1B,UAAU,MAAM,KACf,OAKF,CAAC"}
@@ -0,0 +1,26 @@
1
+ export const branchProvider = (branch) => {
2
+ if (branch == null) {
3
+ return undefined;
4
+ }
5
+ if (branch.accounting != null) {
6
+ return branch.accounting;
7
+ }
8
+ return branch.exactAccounting ? 'exact' : undefined;
9
+ };
10
+ export const branchHasAccounting = (branch) => branchProvider(branch) != null;
11
+ export const tenantHasAccounting = (tenant) => {
12
+ if (tenant == null) {
13
+ return false;
14
+ }
15
+ if (tenant.exactAccounting) {
16
+ return true;
17
+ }
18
+ return tenant.branches.some((b) => branchHasAccounting(b));
19
+ };
20
+ export const branchHasAccountingInTenant = (tenant, branchId) => {
21
+ if (tenant == null) {
22
+ return false;
23
+ }
24
+ return branchHasAccounting(tenant.branches.find((b) => b.id === branchId));
25
+ };
26
+ //# sourceMappingURL=accounting.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounting.js","sourceRoot":"/","sources":["utils/accounting.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,MAA0B,EAAkC,EAAE;IAC3F,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,IAAI,MAAM,CAAC,UAAU,IAAI,IAAI,EAAE,CAAC;QAC9B,OAAO,MAAM,CAAC,UAAU,CAAC;IAC3B,CAAC;IACD,OAAO,MAAM,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA0B,EAAW,EAAE,CACzE,cAAc,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;AAEjC,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,MAA0B,EAAW,EAAE;IACzE,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC,CAAC;AAQF,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,MAA0B,EAC1B,QAAgB,EACP,EAAE;IACX,IAAI,MAAM,IAAI,IAAI,EAAE,CAAC;QACnB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,mBAAmB,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,QAAQ,CAAC,CAAC,CAAC;AAC7E,CAAC,CAAC","sourcesContent":["import { AccountingProvider, Branch, Tenant } from '../types/tenants.js';\n\nexport const branchProvider = (branch: Branch | undefined): AccountingProvider | undefined => {\n if (branch == null) {\n return undefined;\n }\n if (branch.accounting != null) {\n return branch.accounting;\n }\n return branch.exactAccounting ? 'exact' : undefined;\n};\n\nexport const branchHasAccounting = (branch: Branch | undefined): boolean =>\n branchProvider(branch) != null;\n\nexport const tenantHasAccounting = (tenant: Tenant | undefined): boolean => {\n if (tenant == null) {\n return false;\n }\n if (tenant.exactAccounting) {\n return true;\n }\n return tenant.branches.some((b) => branchHasAccounting(b));\n};\n\n/**\n * Whether a specific branch books through an accounting integration. Resolves\n * purely per branch — a tenant can mix accounting and non-accounting branches.\n * Accounting is carried on the branch (`accounting`, or the deprecated\n * `exactAccounting`), so the tenant-wide flag is not consulted.\n */\nexport const branchHasAccountingInTenant = (\n tenant: Tenant | undefined,\n branchId: string,\n): boolean => {\n if (tenant == null) {\n return false;\n }\n return branchHasAccounting(tenant.branches.find((b) => b.id === branchId));\n};\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=accounting.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounting.test.d.ts","sourceRoot":"/","sources":["utils/accounting.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ import { branchHasAccounting, branchHasAccountingInTenant, branchProvider, tenantHasAccounting, } from './accounting.js';
2
+ const makeBranch = (overrides = {}) => ({
3
+ id: 'b1',
4
+ name: 'Branch 1',
5
+ country: 'ci',
6
+ language: 'fr',
7
+ currency: 'XOF',
8
+ adminNumberPrefix: '',
9
+ stockNumberPrefix: '',
10
+ vatNumber: '',
11
+ vatRates: { standard: '18' },
12
+ location: { country: 'ci' },
13
+ ...overrides,
14
+ });
15
+ const makeTenant = (branches, overrides = {}) => ({
16
+ id: 't',
17
+ name: 't',
18
+ region: 'eu-west-1',
19
+ defaultCountry: 'ci',
20
+ defaultCurrency: 'XOF',
21
+ defaultLanguage: 'fr',
22
+ subdomains: ['t'],
23
+ languages: ['fr'],
24
+ emailDomain: 'example.com',
25
+ branches: branches,
26
+ modules: [],
27
+ opportunities: { types: [] },
28
+ ...overrides,
29
+ });
30
+ describe('branchProvider', () => {
31
+ it('returns undefined for missing branch', () => {
32
+ expect(branchProvider(undefined)).toBeUndefined();
33
+ });
34
+ it('prefers the new discriminator over the legacy boolean', () => {
35
+ const branch = makeBranch({ accounting: 'odoo', exactAccounting: true });
36
+ expect(branchProvider(branch)).toBe('odoo');
37
+ });
38
+ it('falls back to legacy boolean as exact', () => {
39
+ const branch = makeBranch({ exactAccounting: true });
40
+ expect(branchProvider(branch)).toBe('exact');
41
+ });
42
+ it('returns undefined when neither field is set', () => {
43
+ expect(branchProvider(makeBranch())).toBeUndefined();
44
+ });
45
+ it('returns undefined when legacy boolean is false', () => {
46
+ expect(branchProvider(makeBranch({ exactAccounting: false }))).toBeUndefined();
47
+ });
48
+ });
49
+ describe('branchHasAccounting', () => {
50
+ it('is true when accounting is set', () => {
51
+ expect(branchHasAccounting(makeBranch({ accounting: 'odoo' }))).toBe(true);
52
+ });
53
+ it('is true when legacy exactAccounting is true', () => {
54
+ expect(branchHasAccounting(makeBranch({ exactAccounting: true }))).toBe(true);
55
+ });
56
+ it('is false otherwise', () => {
57
+ expect(branchHasAccounting(makeBranch())).toBe(false);
58
+ expect(branchHasAccounting(undefined)).toBe(false);
59
+ });
60
+ });
61
+ describe('tenantHasAccounting', () => {
62
+ it('is true when tenant-level legacy flag is set', () => {
63
+ const tenant = makeTenant([makeBranch()], { exactAccounting: true });
64
+ expect(tenantHasAccounting(tenant)).toBe(true);
65
+ });
66
+ it('is true when any branch has accounting', () => {
67
+ const tenant = makeTenant([makeBranch(), makeBranch({ id: 'b2', accounting: 'odoo' })]);
68
+ expect(tenantHasAccounting(tenant)).toBe(true);
69
+ });
70
+ it('is false when no branch has accounting and tenant flag is not set', () => {
71
+ expect(tenantHasAccounting(makeTenant([makeBranch()]))).toBe(false);
72
+ expect(tenantHasAccounting(undefined)).toBe(false);
73
+ });
74
+ });
75
+ describe('branchHasAccountingInTenant', () => {
76
+ it('resolves per branch in a mixed tenant', () => {
77
+ const tenant = makeTenant([
78
+ makeBranch({ id: 'odoo-branch', accounting: 'odoo' }),
79
+ makeBranch({ id: 'plain-branch' }),
80
+ ]);
81
+ expect(branchHasAccountingInTenant(tenant, 'odoo-branch')).toBe(true);
82
+ expect(branchHasAccountingInTenant(tenant, 'plain-branch')).toBe(false);
83
+ });
84
+ it('ignores the deprecated tenant-wide flag and resolves per branch', () => {
85
+ const tenant = makeTenant([makeBranch({ id: 'plain-branch' })], { exactAccounting: true });
86
+ expect(branchHasAccountingInTenant(tenant, 'plain-branch')).toBe(false);
87
+ });
88
+ it('is false for an unknown branch or missing tenant', () => {
89
+ expect(branchHasAccountingInTenant(makeTenant([makeBranch()]), 'nope')).toBe(false);
90
+ expect(branchHasAccountingInTenant(undefined, 'b1')).toBe(false);
91
+ });
92
+ });
93
+ //# sourceMappingURL=accounting.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"accounting.test.js","sourceRoot":"/","sources":["utils/accounting.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { Branch, Tenant } from '../types/tenants.js';\nimport {\n branchHasAccounting,\n branchHasAccountingInTenant,\n branchProvider,\n tenantHasAccounting,\n} from './accounting.js';\n\nconst makeBranch = (overrides: Partial<Branch> = {}): Branch => ({\n id: 'b1',\n name: 'Branch 1',\n country: 'ci',\n language: 'fr',\n currency: 'XOF',\n adminNumberPrefix: '',\n stockNumberPrefix: '',\n vatNumber: '',\n vatRates: { standard: '18' },\n location: { country: 'ci' },\n ...overrides,\n});\n\nconst makeTenant = (branches: Branch[], overrides: Partial<Tenant> = {}): Tenant =>\n ({\n id: 't',\n name: 't',\n region: 'eu-west-1',\n defaultCountry: 'ci',\n defaultCurrency: 'XOF',\n defaultLanguage: 'fr',\n subdomains: ['t'],\n languages: ['fr'],\n emailDomain: 'example.com',\n branches: branches as [Branch, ...Branch[]],\n modules: [],\n opportunities: { types: [] },\n ...overrides,\n }) as Tenant;\n\ndescribe('branchProvider', () => {\n it('returns undefined for missing branch', () => {\n expect(branchProvider(undefined)).toBeUndefined();\n });\n\n it('prefers the new discriminator over the legacy boolean', () => {\n const branch = makeBranch({ accounting: 'odoo', exactAccounting: true });\n expect(branchProvider(branch)).toBe('odoo');\n });\n\n it('falls back to legacy boolean as exact', () => {\n const branch = makeBranch({ exactAccounting: true });\n expect(branchProvider(branch)).toBe('exact');\n });\n\n it('returns undefined when neither field is set', () => {\n expect(branchProvider(makeBranch())).toBeUndefined();\n });\n\n it('returns undefined when legacy boolean is false', () => {\n expect(branchProvider(makeBranch({ exactAccounting: false }))).toBeUndefined();\n });\n});\n\ndescribe('branchHasAccounting', () => {\n it('is true when accounting is set', () => {\n expect(branchHasAccounting(makeBranch({ accounting: 'odoo' }))).toBe(true);\n });\n\n it('is true when legacy exactAccounting is true', () => {\n expect(branchHasAccounting(makeBranch({ exactAccounting: true }))).toBe(true);\n });\n\n it('is false otherwise', () => {\n expect(branchHasAccounting(makeBranch())).toBe(false);\n expect(branchHasAccounting(undefined)).toBe(false);\n });\n});\n\ndescribe('tenantHasAccounting', () => {\n it('is true when tenant-level legacy flag is set', () => {\n const tenant = makeTenant([makeBranch()], { exactAccounting: true });\n expect(tenantHasAccounting(tenant)).toBe(true);\n });\n\n it('is true when any branch has accounting', () => {\n const tenant = makeTenant([makeBranch(), makeBranch({ id: 'b2', accounting: 'odoo' })]);\n expect(tenantHasAccounting(tenant)).toBe(true);\n });\n\n it('is false when no branch has accounting and tenant flag is not set', () => {\n expect(tenantHasAccounting(makeTenant([makeBranch()]))).toBe(false);\n expect(tenantHasAccounting(undefined)).toBe(false);\n });\n});\n\ndescribe('branchHasAccountingInTenant', () => {\n it('resolves per branch in a mixed tenant', () => {\n const tenant = makeTenant([\n makeBranch({ id: 'odoo-branch', accounting: 'odoo' }),\n makeBranch({ id: 'plain-branch' }),\n ]);\n expect(branchHasAccountingInTenant(tenant, 'odoo-branch')).toBe(true);\n expect(branchHasAccountingInTenant(tenant, 'plain-branch')).toBe(false);\n });\n\n it('ignores the deprecated tenant-wide flag and resolves per branch', () => {\n const tenant = makeTenant([makeBranch({ id: 'plain-branch' })], { exactAccounting: true });\n expect(branchHasAccountingInTenant(tenant, 'plain-branch')).toBe(false);\n });\n\n it('is false for an unknown branch or missing tenant', () => {\n expect(branchHasAccountingInTenant(makeTenant([makeBranch()]), 'nope')).toBe(false);\n expect(branchHasAccountingInTenant(undefined, 'b1')).toBe(false);\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bisondesk/core-sdk",
3
- "version": "1.0.608",
3
+ "version": "1.0.610",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "author": "TheTruckCompany",
package/src/constants.ts CHANGED
@@ -1000,8 +1000,14 @@ export const AXLE_CONFIG: { [config: string]: VehicleAxle[] } = {
1000
1000
  ],
1001
1001
  };
1002
1002
 
1003
+ // The operational debtors index / browse table keeps 6 months. The leasing report's
1004
+ // deeper history lives in a separate `debtors_history` index (see DEBTORS_HISTORY_MONTHS).
1003
1005
  export const DEBTORS_MONTHS_UNTIL_TODAY_COUNT = 6;
1004
1006
 
1007
+ // Depth of the separate `debtors_history` index that backs the leasing report. 24 covers
1008
+ // "last year" and its YoY (which needs the December of two years ago, up to 24 months back).
1009
+ export const DEBTORS_HISTORY_MONTHS = 24;
1010
+
1005
1011
  // Email Template IDs
1006
1012
  export const VEHICLE_SHARE_TEMPLATE_ID = 'vehicle-share';
1007
1013
  export const VEHICLE_MARKETING_CAMPAIGN_TEMPLATE_ID = 'vehicle-marketing-campaign';
@@ -14,6 +14,9 @@ export type MonthData = {
14
14
  overdueInvoiceIds: string[];
15
15
  riskLevel: number;
16
16
  monthlyInvoicedAmount: string;
17
+ // Non-leasing ("extra") debt for this month, computed as-of month-end.
18
+ nonLeasingOverdueAmount: string;
19
+ nonLeasingUnpaidAmount: string;
17
20
  };
18
21
 
19
22
  export type DebtorInfo = {
@@ -47,6 +47,7 @@ export enum OpportunityActions {
47
47
  UNDO_DELIVER_VEHICLE = 'undo_deliver_vehicle',
48
48
  SET_DEAL_INFO = 'update_deal_info',
49
49
  ISSUE_INVOICE = 'issue_invoice',
50
+ CREATE_MANUAL_SALES_INVOICE = 'create_manual_sales_invoice',
50
51
  }
51
52
 
52
53
  export enum OpportunityPaymentStatus {
@@ -315,6 +316,14 @@ export type QuoteInfo = {
315
316
  status: QuoteStatus;
316
317
  roi: string;
317
318
  id: string;
319
+ // Leasing-only metrics, denormalized from the active leasing quote so the leasing report
320
+ // can take medians without re-reading each quote. Absent on non-leasing quotes.
321
+ /** Leasing term in months (`calculatorInput.leasing.duration`). */
322
+ durationMonths?: number;
323
+ /** Yearly ROI as a percentage 0–100 (`calculatorOutput.roi.perYear`). */
324
+ roiPerYear?: number;
325
+ /** Advance (deposit) as a percentage 0–100 (`calculatorInput.leasing.depositPercentage`). */
326
+ advancePercentage?: number;
318
327
  };
319
328
 
320
329
  export type SearchOpportunity = {
@@ -340,6 +349,9 @@ export type SearchOpportunity = {
340
349
  // Calendar days from min(marketingAt, evaluationAt, purchaseAgreedAt) until validatedAt.
341
350
  // Mirrors `daysInVirtualInventory` on the vehicle Business Overview page.
342
351
  daysAvailable?: number;
352
+ // Operational supplier for reporting. For SLB clones this is the source vehicle supplier,
353
+ // not the bank supplier on the clone purchase.
354
+ realSupplierId?: string;
343
355
  };
344
356
  };
345
357
 
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Leasing report — the payload for the `Reports → Leasing` tab. Computed entirely from
3
+ * OpenSearch: receivables (`finance_docs`), contracts (`leasing_contracts`), leasing
4
+ * opportunities (`opportunities`) and client risk (`debtors`).
5
+ *
6
+ * Every metric except the fixed cumulative chart is evaluated over — or as of the end
7
+ * of — the selected whole-month window. `asOf = window.end`: `now` for `mtd`/`ytd`, and
8
+ * the period boundary for historical ranges (so "last year" shows the state at the end
9
+ * of last year). Flow metrics (invoiced, contracts started/ended, opportunities) sum over
10
+ * the window; point-in-time metrics (overdue, aging, active contracts, risk) are
11
+ * reconstructed as of `asOf`.
12
+ *
13
+ * The API returns raw numbers only; the client formats (€, %) and colors the deltas.
14
+ */
15
+
16
+ import { YoyMetric } from './reports-sales.js';
17
+
18
+ /**
19
+ * Period presets driving every metric except the fixed cumulative chart, in selector
20
+ * order. All are month-aligned (contracts start on the 1st of a month), so there are no
21
+ * day-based ranges. `mtd`/`ytd` are to-date; `last{3,6,12}Months` are complete months
22
+ * from the first of a month back to the start of the current month; `lastMonth`/
23
+ * `lastYear` are the previous complete calendar month/year.
24
+ */
25
+ export const LEASING_PERIOD_KEYS = [
26
+ 'mtd',
27
+ 'ytd',
28
+ 'lastMonth',
29
+ 'last3Months',
30
+ 'last6Months',
31
+ 'last12Months',
32
+ 'lastYear',
33
+ ] as const;
34
+
35
+ export type LeasingPeriodKey = (typeof LEASING_PERIOD_KEYS)[number];
36
+
37
+ export const DEFAULT_LEASING_PERIOD: LeasingPeriodKey = 'mtd';
38
+
39
+ export type LeasingReportRequest = { period: LeasingPeriodKey };
40
+
41
+ export type { YoyMetric };
42
+
43
+ /** Outstanding-invoice age bands, in calendar months from issue date as of the window end. */
44
+ export type LeasingAgingBucketKey = '0-1' | '1-2' | '2-3' | '3-12' | '1y+';
45
+
46
+ export type LeasingAgingBucket = {
47
+ bucket: LeasingAgingBucketKey;
48
+ /** Outstanding amount (excl VAT) in this age band, as of the window end. */
49
+ amount: number;
50
+ };
51
+
52
+ export type LeasingCategorySplit = {
53
+ /** Vehicle category from `vehicle.external.general.category` (e.g. "Tractor Head"). */
54
+ category: string;
55
+ count: number;
56
+ /** Share of opportunities into preparation in the current window, 0–100. */
57
+ sharePct: number;
58
+ };
59
+
60
+ export type LeasingSalesperson = {
61
+ /** User id from `opportunity.accountManager`; the client resolves name + avatar. */
62
+ id: string;
63
+ units: number;
64
+ yoyPct: number | null;
65
+ };
66
+
67
+ export type LeasingOppCountry = {
68
+ /** ISO country code (lowercase) from `org.countryCode`. */
69
+ code: string;
70
+ units: number;
71
+ /** Share of opportunities into preparation in the current window, 0–100. */
72
+ sharePct: number;
73
+ };
74
+
75
+ export type LeasingRealSupplier = {
76
+ /** CRM organization id for the operational supplier. */
77
+ id: string;
78
+ /** CRM organization name at report generation time; falls back to `id` when unavailable. */
79
+ name: string;
80
+ units: number;
81
+ /** Share of opportunities with a resolved real supplier in the current window, 0–100. */
82
+ sharePct: number;
83
+ yoyPct: number | null;
84
+ };
85
+
86
+ export type LeasingCumulativeBucket = {
87
+ /** Calendar month, 1–12. */
88
+ month: number;
89
+ /** Cumulative new contracts started this calendar year through this month. */
90
+ thisYear: number;
91
+ /** Cumulative new contracts started last calendar year through this month. */
92
+ lastYear: number;
93
+ };
94
+
95
+ export type LeasingOverdueBucket = {
96
+ /** `YYYY-MM` month. */
97
+ month: string;
98
+ /** Overdue rent amount (excl VAT) as of the end of that month. */
99
+ amount: number;
100
+ };
101
+
102
+ export type LeasingReportResponse = {
103
+ period: {
104
+ key: LeasingPeriodKey;
105
+ /** Current window, half-open `[start, end)` (ISO strings). `end` is `asOf`. */
106
+ start: string;
107
+ end: string;
108
+ /** Number of days in the window. */
109
+ days: number;
110
+ /** Year-ago window `[yoyStart, yoyEnd)`. */
111
+ yoyStart: string;
112
+ yoyEnd: string;
113
+ };
114
+ receivables: {
115
+ /** Net invoiced (invoices − credit notes), excl VAT, over the window. */
116
+ invoiced: YoyMetric;
117
+ /** Overdue (excl VAT) as of the window end (+ as of end − 1y). */
118
+ overdue: YoyMetric;
119
+ /**
120
+ * `overdue / total outstanding` as a fraction 0–1, both from finance_docs as of the window
121
+ * end (overdue ÷ all unpaid rent). Null when nothing is outstanding.
122
+ */
123
+ overdueShareOfOutstanding: number | null;
124
+ /**
125
+ * Share of total outstanding rent held by the 10 largest debtors (concentration), as a
126
+ * fraction 0–1. Null when nothing is outstanding.
127
+ */
128
+ top10ClientShare: number | null;
129
+ };
130
+ /** Outstanding leasing invoices by issue-date age, as of the window end. */
131
+ aging: LeasingAgingBucket[];
132
+ contracts: {
133
+ /** Active contracts as of the window end (+ as of end − 1y). */
134
+ active: YoyMetric;
135
+ /** New contracts started over the window. */
136
+ started: YoyMetric;
137
+ /** Contracts that ended over the window (actual end date in range, excludes discarded). */
138
+ ended: YoyMetric;
139
+ };
140
+ opportunities: {
141
+ /** Leasing opportunities entering Preparation over the window. */
142
+ intoPreparation: YoyMetric;
143
+ /** Median opportunity amount over the window. */
144
+ medianPrice: YoyMetric;
145
+ /** Median leasing term in months, from each opp's active leasing quote. */
146
+ medianDuration: YoyMetric;
147
+ /** Median yearly ROI as a percentage 0–100, from each opp's active leasing quote. */
148
+ medianRoiPerYear: YoyMetric;
149
+ /** 75th-percentile yearly ROI (%) over the current window; null if no quotes. */
150
+ roiPerYearP75: number | null;
151
+ /** Median advance (deposit) as a percentage 0–100, from each opp's active leasing quote. */
152
+ medianAdvancePct: YoyMetric;
153
+ /** Vehicle category split of opportunities into Preparation (current window). */
154
+ categorySplit: LeasingCategorySplit[];
155
+ /** Sellers ranked by opportunities into Preparation (current units desc), with YoY. */
156
+ salespeople: LeasingSalesperson[];
157
+ /** Real suppliers ranked by opportunities into Preparation (current units desc), with YoY. */
158
+ realSuppliers: LeasingRealSupplier[];
159
+ /** Org-country split of opportunities into Preparation (current window). */
160
+ countries: LeasingOppCountry[];
161
+ };
162
+ // Client risk, as of the snapshot month. `riskLevel` is zero-inflated (most clients
163
+ // are 0), so this is the at-risk tail shape, not a central statistic.
164
+ risk: {
165
+ /** Active-contract clients with non-zero risk, with YoY. */
166
+ clientsAtRisk: YoyMetric;
167
+ /** Total active-contract clients (the at-risk denominator). */
168
+ activeClients: number;
169
+ /** Severity-band counts of at-risk clients (`riskLevel` floored: levels 1/2/3/4+). */
170
+ bands: { band: string; count: number }[];
171
+ };
172
+ /** Fixed 12-month cumulative new contracts, this calendar year vs last. */
173
+ cumulativeContracts: LeasingCumulativeBucket[];
174
+ /** Fixed trailing-6-month overdue rent amount (excl VAT), as of each month-end. */
175
+ overdueHistory: LeasingOverdueBucket[];
176
+ /** Track-and-trace fleet usage over the window. */
177
+ fleet: {
178
+ /**
179
+ * Median `last30DaysKm` across wired track-and-trace devices that pinged within the window;
180
+ * null when no such device. From the `track_and_trace_devices` index.
181
+ */
182
+ medianKm30d: number | null;
183
+ /** Number of wired devices (carrying a km value) the median was taken over. */
184
+ deviceCount: number;
185
+ };
186
+ };
@@ -1,8 +1,9 @@
1
1
  /**
2
- * Sales report — the payload for the `Reports → Sales` tab. Everything is computed
3
- * from the per-tenant `opportunities` OpenSearch index, keyed on
2
+ * Sales report — the payload for the `Reports → Sales` tab. Sales-side metrics
3
+ * are computed from the per-tenant `opportunities` OpenSearch index, keyed on
4
4
  * `opportunity.reviewedAt` (the moment an opportunity ENTERS Preparation — our
5
- * "sale" event; `preparedAt` is when it leaves Preparation into Delivery).
5
+ * "sale" event; `preparedAt` is when it leaves Preparation into Delivery). The
6
+ * buyer ranking comes from the vehicles index, keyed on `purchase.purchaseAgreedAt`.
6
7
  *
7
8
  * The API returns raw numbers only; the client formats (€, %, pp, "younger") and
8
9
  * colors the deltas.
@@ -66,6 +67,20 @@ export type SalesReportSalesperson = {
66
67
  yoyPct: number | null;
67
68
  };
68
69
 
70
+ export type SalesReportBuyer = {
71
+ /** User id from `purchase.purchasedBy`; the client resolves name + avatar. */
72
+ id: string;
73
+ units: number;
74
+ yoyPct: number | null;
75
+ };
76
+
77
+ export type SalesReportMake = {
78
+ /** Vehicle make name from `vehicle.external.general.make` (stored verbatim, e.g. "DAF"). */
79
+ make: string;
80
+ units: number;
81
+ yoyPct: number | null;
82
+ };
83
+
69
84
  /** A conglomerate-flagged organization (there is no group/parent model — each org stands alone). */
70
85
  export type SalesReportConglomerateOrg = {
71
86
  /** `org.id`. */
@@ -116,8 +131,12 @@ export type SalesReportResponse = {
116
131
  };
117
132
  /** Top countries by units in the current window. */
118
133
  countries: SalesReportCountry[];
134
+ /** Top vehicle makes by units in the current window (the client slices to its top N). */
135
+ makes: SalesReportMake[];
119
136
  /** All sales people with units in either window (capped; the client list scrolls). */
120
137
  salespeople: SalesReportSalesperson[];
138
+ /** All purchasers with vehicles bought in the current window (capped; the client list scrolls). */
139
+ buyers: SalesReportBuyer[];
121
140
  /** Returning-customer tenure shares (4 buckets). */
122
141
  returning: TenureBucket[];
123
142
  conglomerate: SalesReportConglomerate;
@@ -61,12 +61,16 @@ export type Tenant = PublicTenant & {
61
61
  };
62
62
  };
63
63
 
64
+ export type AccountingProvider = 'exact' | 'odoo';
65
+
64
66
  export type Branch = {
65
67
  id: string;
66
68
  name: string;
67
69
  country: string;
68
70
  language: string;
71
+ /* deprecated — use `accounting === 'exact'` instead. Kept until prod cutover. */
69
72
  exactAccounting?: boolean;
73
+ accounting?: AccountingProvider;
70
74
  currency: string;
71
75
  adminNumberPrefix: string;
72
76
  stockNumberPrefix: string;
@@ -260,6 +260,7 @@ export type SearchVehicle = {
260
260
  dealStatus?: VehiclePurchaseDealStatus;
261
261
  logisticsStatus?: VehiclePurchaseLogisticsStatus;
262
262
  purchasedBy?: string;
263
+ purchaseAgreedAt?: string;
263
264
  parkingName?: string;
264
265
  organizationId: string;
265
266
  organizationExternalId?: number;